[ARC 122]
阅读原文时间:2023年07月10日阅读:3

最近状态差到爆炸.

\(AT\)连掉两把分,啥时候能上黄啊。

\(A\)

考虑直接动归。

把\(O(n^2)\)的动归后缀和优化成\(O(n)\)

A

#include<iostream>
#include<cstdio>
#define ll long long
#define N 100005
#define mod 1000000007

ll a[N],f[N],g[N],sf[N],sg[N];
ll n,ans,sum;

int main(){
    scanf("%lld",&n);
    for(int i = 1;i <= n;++i)
    scanf("%lld",&a[i]),sum = (sum + a[i]) % mod;
    //f:i为-时从i到n有多少种合法方案,g:这些合法方案的权数。
    f[n] = 1,g[n] = (-2 * a[n] + mod) % mod;
    f[n + 1] = 1;
    sf[n] = 2,sf[n + 1] = 1,sg[n] = g[n];
    for(int i = n - 1;i >= 2;--i){
        f[i] = sf[i + 2];
        g[i] = (f[i] * (-2 * a[i] + mod) % mod + sg[i + 2]) % mod;
        sf[i] = (sf[i + 1] + f[i]) % mod;
        sg[i] = (sg[i + 1] + g[i]) % mod;
    }
//    for(int i = n;i >= 2;--i)
//    std::cout<<f[i]<<" "<<g[i]<<std::endl;
    for(int i = n;i >= 2;--i)
    ans = (ans + sum * f[i] % mod + g[i]) % mod;
    ans = (ans + sum) % mod;
    std::cout<<ans<<std::endl;
} 

\(B\)

听说B是一个结论题,正解来看呢,应该是把\(n\)个可取值都试一遍,但是我写的模拟退火。

B

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#define ll long long
#define N 100005

struct P{int x,y,w;}p[N];

ll n;

double ansx,ansy;

inline  double find(double x){
    double ans = 0;
    for(int i = 1;i <= n;++i)
    ans += (p[i].x + x - std::min((double)p[i].x,(double)(2 * x))) / (1.0 * n);
    return ans;
}

int main(){
//    freopen("q.in","r",stdin);
//    freopen("q.out","w",stdout);
    srand(273352);
    scanf("%lld",&n);
    for(int i = 1;i <= n;++i){
        scanf("%d",&p[i].x);
        ansx += p[i].x; }
    ansx = (ansx) / (1.0 * n);
    ansy = 0x7f7f7f7f;
    double eps = 1e-15;
    double T = 200;//初始温度
    while(T > eps && ((double)(clock())/CLOCKS_PER_SEC)<1.9){//终止态
//        std::cout<<T<<" "<<(rand() * 2 - RAND_MAX) * T<<std::endl;
        double nowx = ansx + ((rand() * 2 - RAND_MAX + 1) * T);//在值域[ansx - t,ansx + t];中挑选一个随机数
        long double z = find(nowx) - find(ansx);
        if(z < 0)
        ansx = nowx,ansy = std::min(ansy,find(nowx));
        else
        if(exp(-z / T) * RAND_MAX > rand())//随机接受
        ansx = nowx;
        T *= 0.997;//降温速率
//        std::cout<<ansx<<std::endl;
    }
    printf("%.12lf\n",ansy);
} 

\(C\)

考虑每一个数在\(fib\)数系下都有唯一分解。

做完了。

C

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long

ll f[200],to[200],ti[200];
ll ans[200];

ll n;
ll k;

//0 1
//1 1
//1 2
//1 3
//4 3
//4 7

int main(){
    scanf("%lld",&n);
    ll q = n;
    ll len = 1;
    f[0] = 1,f[1] = 1;
    while(f[len] <= n + 1){
        f[++len] = f[len - 1] + f[len - 2];
    }
    for(int i = len;i >= 1;--i)
    if(n >= f[i])
    to[++to[0]] = i,n -= f[i];
    std::sort(to + 1,to + to[0] + 1);
    for(int i = 1;i <= to[0];++i)
    ti[i] = to[to[0]] - to[i];
    std::sort(ti + 1,ti + to[0] + 1);
    ll s = to[to[0]];
    ll x = 0,y = 0;
    ll now = 1;
    for(int i = 0;i <= s;++i){
        if(i == ti[now]){
            if(i & 1)
            ans[++k] = 1,x += 1;
            else
            ans[++k] = 2,y += 1;
            ++now;
        }
        if(i & 1)
        ans[++k] = 4,y += x;
        else
        ans[++k] = 3,x += y;
    }
    std::cout<<k<<std::endl;
    if(x == q){
        for(int i = 1;i <= k;++i)
        std::cout<<ans[i]<<std::endl;
    }
    else{
        for(int i = 1;i <= k;++i){
            if(ans[i] <= 2)
            std::cout<<3 - ans[i]<<std::endl;
            else
            std::cout<<7 - ans[i]<<std::endl;
        }
    }
} 

\(D\)

考虑如果后手已经想好的每个数对,那么其实游戏进程没有差别。

所以这是一个假博弈。

我们考虑对每一位进行操作,如果这一位的\(0\),\(1\)的数量都是偶数,那么递归进子树操作。

否则则把左子树和右子树左右匹配,用\(01tire\)找出最小的匹配,因为再往下递归的所有对都小于这个匹配。

D

#include<iostream>
#include<cstdio>
#define ll long long

ll to[12000005][2];
ll cnt[12000005];

ll n;

ll dfncnt;

inline void insert(ll x){
    ll u = 0;
    cnt[u] ++ ;
    for(int i = 29;i >= 0;--i){
        int t = (x >> i) & 1;
        if(!to[u][t])
        to[u][t] = ++ dfncnt;
        u = to[u][t];
        cnt[u] ++ ;
    }
} 

ll ans = 0,tmp;

#define l(u) to[u][0]
#define r(u) to[u][1]

inline void find(ll p1,ll p2,ll now,ll dep){
//    std::cout<<p1<<" "<<p2<<" "<<now<<" "<<dep<<std::endl;
    if(dep == 0){
        tmp = std::min(now,tmp);
        return;
    }
    bool q = 0;
    for(int i = 0;i <= 1;++i)
    if(to[p1][i] && to[p2][i]){
        q = 1;
        find(to[p1][i],to[p2][i],now,dep - 1);
    }
    if(q)
    return;
    for(int i = 0;i <= 1;++i)
    if(to[p1][i] && to[p2][!i]){
        find(to[p1][i],to[p2][!i],now | (1 << (dep - 1)),dep - 1);
        q = true;
    }
}

inline void dfs(int u,int dep){
//    std::cout<<u<<" "<<dep<<std::endl;
    if(!to[u][1] && !to[u][0])
    return;
    if(cnt[l(u)] % 2 && cnt[r(u)]){
        tmp = (1 << 30);
        find(l(u),r(u),(1 << (dep - 1)),dep - 1);
        ans = std::max(ans,tmp);
        return ;
    }
    if(l(u))
    dfs(l(u),dep - 1);
    if(r(u))
    dfs(r(u),dep - 1);
}

int main(){
    scanf("%lld",&n);
    for(int i = 1;i <= 2 * n;++i){
        ll x;
        scanf("%lld",&x);
        insert(x);
    }
    dfs(0,30);
    std::cout<<ans<<std::endl;
} 

\(E\)

由于\(lcm(x,y) = \frac{x * y}{gcd(x,y)}\)

考虑最后一个数,那么有\(gcd(lcm(a_j),a_i) < a_i\)

即\(lcm(gcd(a_i,a_j)) < a_i\)这里是由于精度所以不能选择前一种(

然后依次从后向前选择就好了。

E

#include<iostream>
#include<cstdio>
#define ll long long
#define N 305

ll ans[N],a[N];
bool vis[N];

ll n;

inline ll g(ll a,ll b){return (b == 0) ? a : g(b,a % b);}

int main(){
    scanf("%lld",&n);
    for(int i = 1;i <= n;++i)
    scanf("%lld",&a[i]);
    for(int i = n;i >= 1;--i){
        bool q;
        for(int j = 1;j <= n;++j){
            ll lcm = 1;
            ll gcd = 1;
            if(!vis[j]){
                q = 1;
                for(int k = 1;k <= n;++k){
                    if(!vis[k] && k != j){
                        gcd = g(a[k],a[j]);
                        lcm = lcm / g(gcd,lcm) * gcd;
                        if(lcm >= a[j]){
                            q = 0;
                            break;
                        }
                    }
                }
                if(q){
                    ans[i] = a[j];
                    vis[j] = 1;
                    break;
                }
            }
        }
        if(!q){
            puts("No");
            return 0;
        }
    }
    puts("Yes");
    for(int i = 1;i <= n;++i)
    std::cout<<ans[i]<<" ";
}