题目描述
20%
暴力+打表
用优美的暴力跑上几个小时就算出n=8的值了
好像前20分都是n=8
50%
不会
100%
真是一道神题。。。
先考虑k=0的情况,那么就是jzoj上的另一道题
3303. 【集训队互测2013】城市规划
正难则反,考虑用容斥原理来解
设f[n]表示有n个点的图,且图连通的方案数
f不好直接求,所以考虑设g[n]表示有n个点的图,且图不要求连通的方案数
为了方便表示,以下均设b表示图大小为n的最大边数,即b=n(n-1)/2
显然g[n]=2bg[n]=2b
至于f[n],可以考虑枚举n所在的连通块大小i
则f[n]=g[n]−∑n−1i=1Ci−1n−1∗f[i]∗g[n−i]f[n]=g[n]−∑i=1n−1Cn−1i−1∗f[i]∗g[n−i]
首先,因为n已经固定,所以乘上组合数来表示i内非n元素的情况
其次,乘上f[i]表示连通块i内的情况
最后,乘上g[n-i]表示剩余部分的情况
很简单?
但是题目要求的是∑k2∑k2,而现在求的是方案数,所以对于f和g都要多设一维,分别表示∑k0到2∑k0到2
之前所求就是f[][0]和g[][0],还差f[][1/2]和g[][1/2]
GDOI2018 滑稽子图跟这题差不多,都用到了二项式展开
∑(a+b)2=∑a2+2ab+b2∑(a+b)2=∑a2+2ab+b2
把f和g当成a和b就可以做了
证明
设f和g的方案集合分别为a/b(去掉第一维,a、b分别表示f和g表示的每种方案的所选边数)
则f[0]=∑ai0f[0]=∑ai0
g[0]=∑bj0g[0]=∑bj0
由已知可得
F[0]=∑∑(ai+bj)0F[0]=∑∑(ai+bj)0(每种方案组合,边数相加)
F[0]=∑∑ai0bj0F[0]=∑∑ai0bj0(因为是0次方,所以可以随便拆)
F[0]=f[0]∗g[0]F[0]=f[0]∗g[0](前面的F和后面的不一样)
F[1]=∑∑(ai+bj)1F[1]=∑∑(ai+bj)1
F[1]=∑ai∑bjF[1]=∑ai∑bj
因为已知ai的个数是f[0],bj的个数是g[0]
所以每个ai都乘了g[0]次,每个bj都乘了f[0]次
F[1]=g[0]∑ai+f[0]∑bjF[1]=g[0]∑ai+f[0]∑bj
F[1]=g[0]∗f[1]+f[0]∗g[1]F[1]=g[0]∗f[1]+f[0]∗g[1]
F[2]=∑∑(ai+bj)2F[2]=∑∑(ai+bj)2
F[2]=∑∑ai2+2ai∗bj+bj2F[2]=∑∑ai2+2ai∗bj+bj2
F[2]=g[0]∑ai2+f[0]∑bi2+∑∑2ai∗bjF[2]=g[0]∑ai2+f[0]∑bi2+∑∑2ai∗bj
F[2]=g[0]∗f[2]+f[0]∗g[2]+∑∑2ai∗bjF[2]=g[0]∗f[2]+f[0]∗g[2]+∑∑2ai∗bj
因为
f[1]∗g[1]=∑∑ai∗bjf[1]∗g[1]=∑∑ai∗bj
所以
F[2]=g[0]∗f[2]+f[0]∗g[2]+2∗f[1]∗g[1]F[2]=g[0]∗f[2]+f[0]∗g[2]+2∗f[1]∗g[1]
得证
求g[][0/1/2]
现在f解决了,问题就是如何求g
g[0]很显然,就是2b2b
g[1]=∑kg[1]=∑k,可知每条边在每个方案中都被算了一次,而一共有b条边,除去已选的边外其它边随便选
所以g[1]=b∗2b−1g[1]=b∗2b−1
g[2]比较蛋疼。。。
因为g[2]=∑k2g[2]=∑k2,考虑往里面填上一条新边
则g[2]=∑(k+1)2g[2]=∑(k+1)2
g[2]=∑k2+2k+1g[2]=∑k2+2k+1
首先,每条边对于每种方案贡献了1,所以一共是b∗2b−1b∗2b−1(就是g[1]的情况)
其次,每条边还贡献了∑2k∑2k
而这个k,是在当前边固定的情况下的k,也就是说是b-1条边下的方案
∑k∑k就是g[0],等于b∗2b−1b∗2b−1
现在的b=b’-1,所以∑2k=2(b−1)∗2b−2∑2k=2(b−1)∗2b−2
一共是b条边,所以贡献为2b(b−1)∗2b−22b(b−1)∗2b−2
但是这样算会有重复。
我们求的是每一条边在每种方案下的贡献,也就是该种方案其它选了的边的影响
但如果在同一方案下,两条边相互影响就会算重
所以∑2k∑2k的实际贡献是b(b−1)∗2b−2b(b−1)∗2b−2
所以
g[2]=b∗2b−1+b(b−1)∗2b−2g[2]=b∗2b−1+b(b−1)∗2b−2
没了?
没了
code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;
long long C[2001][2001];
long long p[4000001];
long long f[2001][3];
long long g[2001][3];
int n,i,j,k,l;
long long b,mod;
int main()
{
scanf("%d%lld",&n,&mod);
C[0][0]=1;
fo(i,1,n)
{
C[i][0]=1;
C[i][i]=1;
fo(j,1,i-1)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
p[0]=1;
fo(i,1,n*(n-1)>>1)
p[i]=(p[i-1]<<1)%mod;
fo(i,0,n)
{
b=i*(i-1)>>1;
g[i][0]=p[b];
if (b>0)
g[i][1]=b*p[b-1]%mod;
if (b>1)
g[i][2]=(b*p[b-1]+b*(b-1)%mod*p[b-2])%mod;
}
g[2][2]=1;
fo(i,1,n)
{
f[i][0]=g[i][0];
f[i][1]=g[i][1];
f[i][2]=g[i][2];
fo(j,1,i-1)
{
f[i][0]=(f[i][0]-C[i-1][j-1]*(f[j][0]*g[i-j][0]%mod))%mod;
f[i][1]=(f[i][1]-C[i-1][j-1]*(f[j][0]*g[i-j][1]%mod+f[j][1]*g[i-j][0]%mod))%mod;
f[i][2]=(f[i][2]-C[i-1][j-1]*(f[j][0]*g[i-j][2]%mod+2*f[j][1]*g[i-j][1]%mod+f[j][2]*g[i-j][0]%mod)%mod)%mod;
}
}
printf("%lld\n",(f[n][2]+mod)%mod);
}