[AGC022F] Checkers 题解
题目链接
题目解法
很妙的题!!!
考虑 是无穷大的数,所以可以认为 的系数是单独的一项,不会和 合并
所以问题转化成了:每个数初始是 ( 可以理解是元),进行题目中的操作,问最后形成的 次多项式的个数
由 向 连边,这会形成一棵树
考虑 的系数,手画一下可以发现为
我们用递推的方式考虑 的值
如果 是已知的,每个儿子会使 ,所以
所以 与 不同只与 儿子的奇偶性有关,也就是说,通过 和 是否相同以及每个节点的深度可以确定一个多项式
考虑一个 ,令 为前若干层共有 个点,当前层有 个点的儿子数量为奇数
枚举下一层的节点个数
那么不考虑儿子数量的奇偶性,下一层节点中和父亲 相同的个数为 ,需要保证
枚举下一层实际和父亲 相同的个数 ,自己手画一下图可以发现,下一层中度数为奇数的节点的个数
这里给出一个结论:只需要从 转移到 ,不能转移到
为什么?第一,根据递推式,我们只关心下一层中实际和父亲相同的点的个数,而不在乎树的形态(就是怎么连,不包括节点深度),而 可以转移到所有 的点,当然是取 ;第二, 题解区有 图
所以转移为:
边界为 ,答案为
时间复杂度
点击查看代码
#include <bits/stdc++.h> #define F(i,x,y) for(int i=(x);i<=(y);i++) #define DF(i,x,y) for(int i=(x);i>=(y);i--) #define ms(x,y) memset(x,y,sizeof(x)) #define SZ(x) (int)x.size()-1 #define pb push_back using namespace std; typedef long long LL; typedef unsigned long long ull; typedef pair<int,int> pii; template<typename T> void chkmax(T &x,T y){ x=max(x,y);} template<typename T> void chkmin(T &x,T y){ x=min(x,y);} inline int read(){ int FF=0,RR=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1; for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48; return FF*RR; } const int N=55,P=1e9+7; int n,f[N][N],C[N][N]; //f[i][j]:有i个点,当前层有j个点儿子个数为奇数的方案数 inline void inc(int &x,int y){ x+=y;if(x>=P) x-=P;} int main(){ n=read(); C[0][0]=1; F(i,1,n){ C[i][0]=C[i][i]=1;F(j,1,i-1) C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;} f[1][0]=f[1][1]=n; F(i,1,n) F(j,0,i) if(f[i][j]) F(k,max(1,j),n-i){//下一层有k个点 if((k-j)&1) continue; int t=(k-j)/2;//不考虑儿子个数的奇偶性,下一层有t个点和父亲符号相同 F(p,0,k) inc(f[i+k][abs(t-p)],1ll*f[i][j]*C[n-i][k]%P*C[k][p]%P);//下一层实际有p个点和父亲符号相同 } printf("%d\n",f[n][0]); return 0; }
能不能再给力一点
我们拆开转移式:
接下来的一步比较妙,令
那么
这里重新定义 为
所以
可以发现, 关系不大,考虑拆开
这里给出转移式:
有了转移式,还要知道 的范围
首先 ,且
综合一下 且
前面的限制都好满足,但 不太好满足
我们钦定 ,这样会漏掉 的情况,暴力转移即可
边界为 ,答案为
时间复杂度
点击查看代码
#include <bits/stdc++.h> #define F(i,x,y) for(int i=(x);i<=(y);i++) #define DF(i,x,y) for(int i=(x);i>=(y);i--) #define ms(x,y) memset(x,y,sizeof(x)) #define SZ(x) (int)x.size()-1 #define pb push_back using namespace std; typedef long long LL; typedef unsigned long long ull; typedef pair<int,int> pii; template<typename T> void chkmax(T &x,T y){ x=max(x,y);} template<typename T> void chkmin(T &x,T y){ x=min(x,y);} inline int read(){ int FF=0,RR=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1; for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48; return FF*RR; } const int N=55,P=1e9+7; int n,f[N][N],g[N][N<<1]; int fac[N],ifac[N],C[N][N]; //f[i][j]:有i个点,当前层有j个点儿子个数为奇数的方案数 inline void inc(int &x,int y){ x+=y;if(x>=P) x-=P;} int qmi(int a,int b){ int res=1; for(;b;b>>=1){ if(b&1) res=1ll*res*a%P;a=1ll*a*a%P;} return res; } int main(){ n=read(); C[0][0]=1; F(i,1,n){ C[i][0]=C[i][i]=1;F(j,1,i-1) C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;} fac[0]=1; F(i,1,n) fac[i]=1ll*fac[i-1]*i%P; ifac[n]=qmi(fac[n],P-2); DF(i,n-1,0) ifac[i]=1ll*ifac[i+1]*(i+1)%P; f[1][0]=f[1][1]=fac[n]; F(i,1,n){ F(j,0,n<<1) if(g[i][j]) F(x,0,n-i) if(x+j-n>=0) if(~(j-n-x)&1) inc(f[i+x][abs((j-n-x)/2)],1ll*g[i][j]*ifac[x]%P); F(j,0,i){ if(f[i][j]) F(y,1,n-i) inc(g[i+y][y-j+n],1ll*f[i][j]*ifac[y]%P); //y=0 F(k,max(1,j),n-i) if(~(k-j)&1){ int t=(k-j)/2; inc(f[i+k][abs(t-k)],1ll*f[i][j]*C[n-i][k]%P*ifac[n-i]%P*fac[n-i-k]%P); } } } printf("%d\n",f[n][0]); return 0; }
标签:
Atcoder
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通