jzoj4424

这里写图片描述
20%:暴力枚舉每一條邊有沒有被選到,然後使用并查集判斷聯通性
這樣子有20分,但是我考試寫掛了所以1分也沒有
100%:這道題2000的數據範圍,使用指數級搜索會tle,需要更加好的方法
這道題中,可以設f[n]表示節點數為n,且整個圖必須得聯通的方案數
g[n]為節點數為n,但是整個圖不必聯通的方案數
則g[n]=2^(n-1)(n)/2,因為我們可以選和不選每一條邊,都可以對方案產生貢獻
而f[n]屬於g[n],因為當一個方案聯通時,它一定不必聯通,但是一個方案不必聯通,則這個方案不一定是聯通的,所以我們可以將g數組減去幾個數得到f數組就可以正確的計算出有多少種方案合法
我們可以知道一個方程,f[n]=g[n]-sigma(i=1~n-1)c(n-1,i-1)*f[i]*g[n-i]
為什麼?我們可以將整個圖分成2個部分,大小分別為i和n-i
我們可以強制性的將聯通塊分到第n節點,然後去掉n節點考慮影響
現在,設包含n的聯通塊的大小為i,則其他部分的大小為n-i。
我們可以不在這些聯通塊中連邊,這樣子圖一定不連通
所以,方案數=強制選擇n,聯通塊大小為i的方案數*選擇i個節點,必須使這i個節點聯通的方案數乘上選擇n-i個節點,不必使這些節點聯通的方案數
也就是c(n-1,i-1)*f[i]*g[n-i]
但是這種方案有一個疑點:萬一我們兩邊圖都可以不連通呢?會不會算漏呢?
不存在的。因為我們選出來這i個節點不連通,但是在由這i個節點組成的點集中,一定有包含節點n的聯通塊,而這種方案已經算過了,所以不會算漏
這樣,我們就求出了由n個節點構成的圖,有多少種方案使整個圖必須聯通
但是,這道題要求的是平方和。所以還要知道怎麼計算這個平方和
f,g數組要設多1維0/1/2,表示統計的是0次方/1次方/2次方和
我們考慮g數組怎麼計算,設d=n*(n-1)/2
則g[n][0]=2^d
g[n][1],設現在連了x條邊,則對答案的貢獻為x*c[d][x],可以加起來算
但是這個方法不夠優,我們可以考慮一下每條邊選了以後,會對答案造成多少的貢獻
所以ans=d*每條邊可以選的次數
其他邊可以隨便亂選,有2^(d-1)種方案
所以ans=d*2^(d-1)
同理,考慮向方案中添加一條邊會變成怎麼樣
我們要計算恰好包含這條邊的方案,就知道多了多少種方案
原來,每一種方案是sigma(i)num[i]^2,設其等於v
現在變成了sigma(i)(num[i]+1)^2
轉化為v+sigma(i)2k+1
sigma(i)1很顯然等於g[n][1]
但是sigma(i)2k所產生的所有方案,當前邊必須固定選,剩下來d-1條邊,所以等於2(d−1)∗2^(d−2)
但是這個算法有點缺陷,因為如果那條邊選了,這條邊也選,再接一些奇怪的方案,與這條邊選了,那條邊也選,再接同一種方案,是一樣的
所以實際上答案只有(d−1)∗2^(d−2)
總共為d∗2^(d−1)+d(d−1)∗2^(d−2)
計算出d=n*(n-1)/2的時候的方案數就是答案g[n][2]
這樣子計算出了g數組
關於f數組怎麼求
設一個輔助數組h,存的是一定使圖不連通的方案數
f[n][0]已經求出來了,所以h[n][0]也求出來了
再設a,b,分別為f[i],g[n-i]數組中方案的集合
則h[n][1]=sigma(i)sigma(j)(a[i]+b[j])
因為當我們將2個方案組合在一起時,新的方案會有a[i]+b[j]條邊,會產生a[i]+b[j]的答案
這樣還是很慢
但是,我們可以發現一個這樣子的東西,每一個a[i],b[i]都被加了g[n-i][0]次和f[n-i][0]次,所以這種方案對h的貢獻為sigma(a)*g[n-i][0]+sigma(b)*g[n-i][1]=f[i][0]*g[n-i][1]+f[n-i][1]*g[n-i][0]
再求出f[n][2]的值
h[n][2]=sigma(i)sigma(j)(a[i]+b[j])^2=sigma(i)sigma(j)(a[i]^2+b[j]^2+2*a[i]*b[j])
我們發現,所有的a[i]^2都被乘了g[n-i][0]次,所有的b[i]^2都被乘了f[n-i][0]次
還剩下一個sigma(i)sigma(j)2*a[i]*b[j]
這個的結果為(a[1]b[1]+a[1]b[2]+……+a[1]b[n]+a[2]b[1]+……+a[n]b[n])*2
提取公因式,變為(a[1]+a[2]+…+a[n])*(b[1]+b[2]+…+b[n])*2=f[i][1]*g[n-i][1]*2
所以h[n][2]=sigma(i)f[i][1]*g[n-i][1]*2+f[i][0]*g[n-i][1]+f[n-i][0]*g[n-i][0]
這樣子,我們就計算出了h[n][2]和h[n][1],與g相減就變為了f
這樣,我們就成功的解決了本題
代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll c[2010][2010],f[2010][3],g[2010][3],n,m;
ll qp(ll x,ll y){
    if(y<0)return 0;
    if(!y)return 1;
    if(y==1)return x%m;
    ll r=qp(x,y/2)%m;
    if(y&1)return r*r%m*x%m;
    else return r*r%m;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=0;i<=n;i++){
        c[i][0]=1;
        for(ll j=1;j<=i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%m;
    }
    f[1][0]=g[1][0]=1;
    for(ll i=2;i<=n;i++){
        ll d=i*(i-1)/2;
        f[i][0]=g[i][0]=qp(2,d);
        f[i][1]=g[i][1]=qp(2,d-1)*d%m;
        f[i][2]=g[i][2]=(qp(2,d-1)*d+d*(d-1)%m*qp(2,d-2))%m;
    }
    for(ll i=2;i<=n;i++)
        for(ll j=1;j<i;j++){
            f[i][0]=(f[i][0]-c[i-1][j-1]*f[j][0]%m*g[i-j][0]%m+m)%m;
            f[i][1]=(f[i][1]-c[i-1][j-1]*(f[j][0]*g[i-j][1]%m+f[j][1]*g[i-j][0]%m)%m+m)%m;
            f[i][2]=(f[i][2]-c[i-1][j-1]*(f[j][0]*g[i-j][2]%m+f[j][1]*g[i-j][1]*2%m+f[j][2]*g[i-j][0]%m)+m)%m;
        }
    printf("%lld\n",(f[n][2]%m+m)%m);
    return 0;
}

转载于:https://www.cnblogs.com/rilisoft/p/10385280.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值