jzoj4424. 【HNOI2016模拟4.2】道路

44 篇文章 0 订阅
32 篇文章 0 订阅

题目描述

这里写图片描述
这里写图片描述

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]n1i=1Ci1n1f[i]g[ni] 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都要多设一维,分别表示 k02 ∑ 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]=aibj 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+2aibj+bj2 F [ 2 ] = ∑ ∑ a i 2 + 2 a i ∗ b j + b j 2
F[2]=g[0]ai2+f[0]bi2+2aibj 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]+2aibj F [ 2 ] = g [ 0 ] ∗ f [ 2 ] + f [ 0 ] ∗ g [ 2 ] + ∑ ∑ 2 a i ∗ b j
因为
f[1]g[1]=aibj f [ 1 ] ∗ g [ 1 ] = ∑ ∑ a i ∗ b j
所以
F[2]=g[0]f[2]+f[0]g[2]+2f[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]=b2b1 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,所以一共是 b2b1 b ∗ 2 b − 1 (就是g[1]的情况)
其次,每条边还贡献了 2k ∑ 2 k
而这个k,是在当前边固定的情况下的k,也就是说是b-1条边下的方案
k ∑ k 就是g[0],等于 b2b1 b ∗ 2 b − 1
现在的b=b’-1,所以 2k=2(b1)2b2 ∑ 2 k = 2 ( b − 1 ) ∗ 2 b − 2
一共是b条边,所以贡献为 2b(b1)2b2 2 b ( b − 1 ) ∗ 2 b − 2


但是这样算会有重复。
我们求的是每一条边在每种方案下的贡献,也就是该种方案其它选了的边的影响

但如果在同一方案下两条边相互影响就会算重
所以 2k ∑ 2 k 的实际贡献是 b(b1)2b2 b ( b − 1 ) ∗ 2 b − 2

所以
g[2]=b2b1+b(b1)2b2 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);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值