jzoj4424. 【HNOI2016模拟4.2】道路

本文探讨了一个特定的图论问题,利用容斥原理进行求解,并详细介绍了如何通过枚举和二项式展开等手段计算图的连通性和边数统计。文章深入分析了不同维度方案数的求解过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

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]n1i=1Ci1n1f[i]g[ni]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都要多设一维,分别表示k02∑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]=aibjF[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+2aibj+bj2F[2]=∑∑ai2+2ai∗bj+bj2
F[2]=g[0]ai2+f[0]bi2+2aibjF[2]=g[0]∑ai2+f[0]∑bi2+∑∑2ai∗bj
F[2]=g[0]f[2]+f[0]g[2]+2aibjF[2]=g[0]∗f[2]+f[0]∗g[2]+∑∑2ai∗bj
因为
f[1]g[1]=aibjf[1]∗g[1]=∑∑ai∗bj
所以
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]很显然,就是2b2b

g[1]=kg[1]=∑k,可知每条边在每个方案中都被算了一次,而一共有b条边,除去已选的边外其它边随便选
所以g[1]=b2b1g[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,所以一共是b2b1b∗2b−1(就是g[1]的情况)
其次,每条边还贡献了2k∑2k
而这个k,是在当前边固定的情况下的k,也就是说是b-1条边下的方案
k∑k就是g[0],等于b2b1b∗2b−1
现在的b=b’-1,所以2k=2(b1)2b2∑2k=2(b−1)∗2b−2
一共是b条边,所以贡献为2b(b1)2b22b(b−1)∗2b−2


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

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

所以
g[2]=b2b1+b(b1)2b2g[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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值