概述
agc022F Checkers
- 有 n n n个数,第 i i i个数一开始为 ( 1 0 100 ) i (10^{100})^i (10100)i,现在进行 n − 1 n-1 n−1次操作,选择两个数 x , y x,y x,y,将它们变为 2 x − y 2x-y 2x−y。
- 求最后的剩下的数有多少种结果。 n ≤ 50 nle50 n≤50
Solution
- 一开始想的是建成一个二叉树,左儿子乘 − 1 -1 −1,右儿子乘 2 2 2,但是我们不知道 2 2 2次幂相同有什么排布规律使得它不算重,所以我们考虑将这个二叉树上的所有 − 1 -1 −1的边缩起来。
- 那么我们会得到一棵树,第 i i i层的数是 + / − 2 i +/-2^i +/−2i。
- 接下来考虑关于符号的树形 D P DP DP。
- 从下往上一层一层地加入点,那么当前的点要么是+,要么是-,考虑如果一个点连上奇数个儿子,那么它的权值会乘上-1,考虑一个点连的儿子是有顺序的,那么一个儿子在次序为奇数会再乘上-1。
- 我们不难发现,对于儿子层的点,假设有 x x x个点要求它的父亲链上对它的影响为+1, y y y个点为-1,那么一个当前层的节点,如果儿子奇数位置为要求+1的点,那么当前点的要求就变成了-1,即我们可以通过儿子来推出父亲的要求。
- 不妨设 x < = y x<=y x<=y,那么有 y − x y-x y−x个新点是奇数个的,剩下的新点的取值可以随便选择,枚举剩下的新点多少个选1,多少个选-1即可。因此我们可以知道,状态只需要记录 ( x − y ) (x-y) (x−y)即可。
- O ( n 4 ) O(n^4) O(n4)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 51
#define ll long long
#define mo 1000000007
using namespace std;
int n,i,j,k,l;
ll f[maxn][maxn][2],C[maxn][maxn],ans;
int main(){
freopen("ceshi.in","r",stdin);
scanf("%d",&n);
for(C[0][0]=1,i=1;i<=n;i++) for(C[i][0]=1,j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
f[0][0][0]=1;
for(i=0;i<n;i++) for(j=0;j<=i;j++) for(int p=0;p<2;p++) if (f[i][j][p]){
int x=(p==1)*j,y=(p==0)*j;
for(k=0;i+j+k<=n;k++) for(l=0;i+j+k+l<=n;l++) if (j+k+l>0) if (i+j+k+l<n||i+j+k+l==n&&j+k+l==1)
(f[i+j+k+l][max(x+k,y+l)-min(x+k,y+l)][x+k<=y+l]+=f[i][j][p]*C[i+j+k+l][i]%mo*C[j+k+l][y+k])%=mo;
}
printf("%lldn",f[n][1][0]);
}
最后
以上就是沉静摩托为你收集整理的agc022F Checkersagc022F Checkers的全部内容,希望文章能够帮你解决agc022F Checkersagc022F Checkers所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复