agc022F

题目大意

题解

非常玄学的dp

首先一个很显然的东西,因为把x按照y翻转得到2y-x,所以每个点的最终系数为±2^k

假设有两种不同的方案得到同样的接,那么相减之后把正的放一边负的放另一边,因为10^100非常大以至于把其他的加在一起都不行,因此只要方案不同答案就不同

转化模型,从y向x连一条边,变成一个由有向边构成的树,每次选一条边u->v,把u所在连通块*2,v所在连通块取反

可以写成一棵权值为2^深度的树,然后从后往前选边,考虑dp,然后就不会了

网上有各种玄学做法,根据个人理解讲一下比较常见的那种


一个点的权值=(-1)^(儿子个数+某个祖先在选完相连链之后选的儿子个数),这样会好考虑一些

由于一个点的权值只会和儿子和祖先有关,因此在最后一层加上一个点后向上只会影响到其父亲,向下的影响等价于放之前父亲祖先的影响,即把点权设为父亲当前的点权,然后把父亲以及之前加入的兄弟节点取反

设f[i,j]表示已经放了i个点,最后一层有j个儿子个数为奇数的方案

假设一个点有s个儿子,那么有s/2上取整(网上的都写成了下取整)个点和父亲不同

枚举当前层的点数k,有(j+k)/2个点与父亲不同

再枚举实际不同的个数l,那么新放的这一层有|l-(j+k)/2|个奇点,乘的系数是C(i+k,i)*C(k,l)

以上是网上的题解,下面开始个人看法


首先如果是简单的“不同”,那么实际上+,-变成-+-,+-的时候当不同个数为2时有+++,+-以及---,--两种情况,这两种都要算进去,这里感觉非常奇怪

并且更加奇怪的是,不同的个数为什么可以直接丢到组合数里面算

所以状态f[i,j]中的j,实际表示的应该是有j个同一种符号奇点的方案数,符号是硬点的某一种(+或-都可以),并且可以改变,具体见下文

还是上面的例子,+,-变成-+-,+-,因为奇点是+所以硬点的符号是+

所以枚举的l的实际意义是当前硬点的符号的个数,由于奇点符号全部相同,所以如果不变(下一层奇点为0)的话那个数就是(j+k)/2,变了之后下一层的奇点个数就是|l-(j+k)/2|

这样就可以在l=0以及l=4把上面的情况区分开来,同时由于l表示的是符号个数所以可以直接组合数

观察一下,如果l>(j+k)/2的话那么是把非硬点符号改成硬点符号,<(j+k)/2是把硬点符号改成非硬点符号,因为改的符号相同且改过的都是奇点,所以下一层的符号就是改过之后的符号,也是唯一确定的

如此归纳下去,最后得到的就是正确的结果

END

时间复杂度O(n^4)

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define C(n,m) ((jc[n]*Jc[m])%1000000007*Jc[(n)-(m)]%1000000007)
#define add(a,b) a=((a)+(b))%1000000007
#define abs(x) ((x)>0?(x):-(x))
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define mod 1000000007
#define Mod 1000000005
#define ll long long
//#define file
using namespace std;

ll f[51][51],jc[51],Jc[51];
int n,i,j,k,l,x;

ll qpower(ll a,int b) {ll ans=1; while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;}

int main()
{
	#ifdef file
	freopen("agc022f.in","r",stdin);
	#endif
	
	scanf("%d",&n);
	jc[0]=1;
	fo(i,1,n) jc[i]=jc[i-1]*i%mod;
	Jc[n]=qpower(jc[n],Mod);
	fd(i,n-1,0) Jc[i]=Jc[i+1]*(i+1)%mod;
	
	f[1][0]=f[1][1]=1;
	fo(i,1,n-1)
	{
		fo(j,0,i)
		{
			fo(k,max(1,j),n-i)
			{
				fo(l,0,k)
				if (!((j+k)&1))
				{
					x=(j+k)/2;
					add(f[i+k][abs(x-l)],f[i][j]*C(i+k,i)%mod*C(k,l));
				}
			}
		}
	}
	printf("%lld
",f[n][0]);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/13736530.html