题目描述
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]=2b g [ n ] = 2 b
至于f[n],可以考虑枚举n所在的连通块大小i
则
f[n]=g[n]−∑n−1i=1Ci−1n−1∗f[i]∗g[n−i]
f
[
n
]
=
g
[
n
]
−
∑
i
=
1
n
−
1
C
n
−
1
i
−
1
∗
f
[
i
]
∗
g
[
n
−
i
]
首先,因为n已经固定,所以乘上组合数来表示i内非n元素的情况
其次,乘上f[i]表示连通块i内的情况
最后,乘上g[n-i]表示剩余部分的情况
很简单?
但是题目要求的是
∑k2
∑
k
2
,而现在求的是方案数,所以对于f和g都要多设一维,分别表示
∑k0到2
∑
k
0
到
2
之前所求就是f[][0]和g[][0],还差f[][1/2]和g[][1/2]
GDOI2018 滑稽子图跟这题差不多,都用到了二项式展开
∑(a+b)2=∑a2+2ab+b2
∑
(
a
+
b
)
2
=
∑
a
2
+
2
a
b
+
b
2
把f和g当成a和b就可以做了
证明
设f和g的方案集合分别为a/b(去掉第一维,a、b分别表示f和g表示的每种方案的所选边数)
则
f[0]=∑ai0
f
[
0
]
=
∑
a
i
0
g[0]=∑bj0
g
[
0
]
=
∑
b
j
0
由已知可得
F[0]=∑∑(ai+bj)0
F
[
0
]
=
∑
∑
(
a
i
+
b
j
)
0
(每种方案组合,边数相加)
F[0]=∑∑ai0bj0
F
[
0
]
=
∑
∑
a
i
0
b
j
0
(因为是0次方,所以可以随便拆)
F[0]=f[0]∗g[0]
F
[
0
]
=
f
[
0
]
∗
g
[
0
]
(前面的F和后面的不一样)
F[1]=∑∑(ai+bj)1
F
[
1
]
=
∑
∑
(
a
i
+
b
j
)
1
F[1]=∑ai∑bj
F
[
1
]
=
∑
a
i
∑
b
j
因为已知ai的个数是f[0],bj的个数是g[0]
所以每个ai都乘了g[0]次,每个bj都乘了f[0]次
F[1]=g[0]∑ai+f[0]∑bj
F
[
1
]
=
g
[
0
]
∑
a
i
+
f
[
0
]
∑
b
j
F[1]=g[0]∗f[1]+f[0]∗g[1]
F
[
1
]
=
g
[
0
]
∗
f
[
1
]
+
f
[
0
]
∗
g
[
1
]
F[2]=∑∑(ai+bj)2
F
[
2
]
=
∑
∑
(
a
i
+
b
j
)
2
F[2]=∑∑ai2+2ai∗bj+bj2
F
[
2
]
=
∑
∑
a
i
2
+
2
a
i
∗
b
j
+
b
j
2
F[2]=g[0]∑ai2+f[0]∑bi2+∑∑2ai∗bj
F
[
2
]
=
g
[
0
]
∑
a
i
2
+
f
[
0
]
∑
b
i
2
+
∑
∑
2
a
i
∗
b
j
F[2]=g[0]∗f[2]+f[0]∗g[2]+∑∑2ai∗bj
F
[
2
]
=
g
[
0
]
∗
f
[
2
]
+
f
[
0
]
∗
g
[
2
]
+
∑
∑
2
a
i
∗
b
j
因为
f[1]∗g[1]=∑∑ai∗bj
f
[
1
]
∗
g
[
1
]
=
∑
∑
a
i
∗
b
j
所以
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]很显然,就是
2b
2
b
g[1]=∑k
g
[
1
]
=
∑
k
,可知每条边在每个方案中都被算了一次,而一共有b条边,除去已选的边外其它边随便选
所以
g[1]=b∗2b−1
g
[
1
]
=
b
∗
2
b
−
1
g[2]比较蛋疼。。。
因为
g[2]=∑k2
g
[
2
]
=
∑
k
2
,考虑往里面填上一条新边
则
g[2]=∑(k+1)2
g
[
2
]
=
∑
(
k
+
1
)
2
g[2]=∑k2+2k+1
g
[
2
]
=
∑
k
2
+
2
k
+
1
首先,每条边对于每种方案贡献了1,所以一共是
b∗2b−1
b
∗
2
b
−
1
(就是g[1]的情况)
其次,每条边还贡献了
∑2k
∑
2
k
而这个k,是在当前边固定的情况下的k,也就是说是b-1条边下的方案
∑k
∑
k
就是g[0],等于
b∗2b−1
b
∗
2
b
−
1
现在的b=b’-1,所以
∑2k=2(b−1)∗2b−2
∑
2
k
=
2
(
b
−
1
)
∗
2
b
−
2
一共是b条边,所以贡献为
2b(b−1)∗2b−2
2
b
(
b
−
1
)
∗
2
b
−
2
但是这样算会有重复。
我们求的是每一条边在每种方案下的贡献,也就是该种方案其它选了的边的影响
但如果在同一方案下,两条边相互影响就会算重
所以
∑2k
∑
2
k
的实际贡献是
b(b−1)∗2b−2
b
(
b
−
1
)
∗
2
b
−
2
所以
g[2]=b∗2b−1+b(b−1)∗2b−2
g
[
2
]
=
b
∗
2
b
−
1
+
b
(
b
−
1
)
∗
2
b
−
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);
}