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";
}