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;
}
jzoj4424的更多相关文章
随机推荐
- xxnet 360浏览器设置
开xxnet全局pac只能代理. 然后选择360浏览器里面使用ie代理设置就行
- window.name跨域实现
参考:window.name实现的跨域数据传输 有三个页面: a.com/app.html:应用页面. a.com/proxy.html:代理文件,一般是一个没有任何内容的html文件,需要和应用页面 ...
- 为什么c++中返回成员变量的指针,会破坏了封装?
上述代码中,get()函数返回的是类成员变量的name的地址,这是很危险的,name是私有的,意味这不想被客户访问,但是如果返回name的地址,那么外部函数就可以修改name,这就破坏了封装性. 为什 ...
- jquery统计显示或隐藏的元素个数
统计显示的checkbox的数量: 统计隐藏的checkbox数量:
- 2018.10.18 NOIP训练 01矩阵(组合数学)
传送门 组合数学好题. 题目要求输出的结果成功把概率转化成了种类数. 本来可以枚举统计最小值为iii时的概率. 现在只需要统计最小值为iii时的方案数,每一行有不少于iii个1的方案数. 显然一行选i ...
- C++ 动态分配 和 内存分配和内存释放
动态分配 动态分配可以说是指针的关键所在.不需要通过定义变量,就可以将指针指向分配的内存.也许这个概念看起来比较模糊,但是确实比较简单.下面的代码示范如何为一个整数分配内存: int *pNumber ...
- 搭建Idea授权服务器用于学习
我自己的搭建服务器http://doit.wenyule.top 懒得看教程或弄不好的小伙伴可以用我搭建的,在激活那选择服务器,输入我上面的地址,注意可以激活2018.2.1之前的.为了防止用的人太多 ...
- Linux 修改 IP地址 和 网关
修改IP地址和网关是很常见的操作,在做相关实验的时候,如果没有设置好,会带来很多不必要的麻烦.. 1. 修改IP地址vi /etc/sysconfig/network-scripts/ifcfg-et ...
- LA 4670 Dominating Patterns (AC自动机)
题意:给定n个字符串和一个文本串,查找哪个字符串出现的次数的最多. 析:一匹配多,很明显是AC自动机.只需要对原来的进行修改一下,就可以得到这个题的答案, 计算过程中,要更新次数,并且要映射字符串.如 ...
- 仿iPhone滑屏操作
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <m ...