ABC243做题笔记

Atcoder Beginner Contest 243

D - Moves on Binary Tree

题目大意

有一棵极大的二叉树,有 \(2^{10^{100}}-1\) 个节点,给定一些操作,输出在线段树上遍历后的最后的节点的编号。

解题思路

如果直接模拟,显然数据太大,会远超出 long long 的范围。

有一个条件非常重要:最终的答案在 long long 范围内,因此考虑:

  • 一次 \(U\) 操作会与一次 \(L\)\(R\) 操作抵消

维护一个栈,遍历操作串的时候每次遇到一个 \(U\) 操作就将其与栈顶中的一个 \(L\)\(R\) 抵消掉。

全部抵消后,直接模拟即可。

for(int i=0;i<n;i++){
    if(s[i]=='U'&&!stk.empty()){
        s[i]='0',s[stk.top()]='0';
        stk.pop();
    }
    else if(s[i]=='L'||s[i]=='R'){
        stk.push(i);
    }
}

//剩余模拟部分略

E - Edge Deletion

题目大意

有一张有 \(N\) 个顶点,有 \(M\) 条边的带边权无向图,问能删多少条边,使得每两点之间的最短路长度不变。

解题思路

注意到这道题中 \(N\) 的范围很小,考虑 floyd 求全源最短路,再松弛边的时候,如果松弛的边距离与当前最短路长度相等,那么显然这条边时可以删去的。

for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(low[i][j]==low[i][k]+low[k][j]&&i!=k&&j!=k){ //如果距离相等
                f[i][j]=1;
            }

            low[i][j]=min(low[i][j],low[i][k]+low[k][j]);
        }

for(int i=1;i<=m;i++){
    //有两种情况可以删边:前面记录的状态可以删去;当前两点之间最短路不经过这条边
    if(f[e[i].u][e[i].v]||low[e[i].u][e[i].v]!=e[i].d){
        ans++;
    }
}    

cout<<ans<<"\n";

G - Sqrt

题目大意

有一个数列,最开始只有一个数 \(X\)。你可以对其进行一种操作:设末尾的数为 \(Y\),从 \(1 \ldots \sqrt Y\) 中选择一个数加到数列的末尾。如此进行 \(10^{100}\) 次操作,问数列一共有多少种可能的状态。

解题思路

有一个 \(O(T + X \sqrt X)\) 的做法,设 \(f_i\) 表示以数字 \(i\) 开头的数列可能的状态,设 \(i\) 的下一个位置上的数为 \(j\),那么 \(f_i = \sum _{j=1}^{ \sqrt i } f_j\)

最终答案为 \(f_x\),由于 \(X\) 极大,这种做法显然会超时。

考虑优化,根据数据范围以及上面的状态转移方程,\(f_x = \sum _{i=1}^{\sqrt [4]{x}}(\sqrt x - k^2+1) \times f_k\)

时间复杂度 \(O(T \times \sqrt [4]{X})\),轻松通过。

为了避免精度问题,需要开 long double。

f[1]=1;
for(int i=2;i<=5e5;i++){
    for(int j=1;j<=sqrt((double)i);j++){
        f[i]+=f[j];
    }
}

while(T--){
    cin>>n;

    int ans=0,k=sqrt((double)n);
    for(int i=1;i<=sqrt((double)k);i++){
        g[i]=f[i]*(k-i*i+1);
        ans+=g[i];
    }

    cout<<ans<<"\n";
}
posted @ 2025-01-15 14:52  SunburstFan1106  阅读(16)  评论(0)    收藏  举报