1. 程式人生 > >bzoj 1494: [NOI2007]生成樹計數

bzoj 1494: [NOI2007]生成樹計數

55555555555555555555555555555555被虐哭了

調了兩個小時才調出來,簡直不要太坑。

論文還是比較良心的,就是初始矩陣好難構造的說。

最後迫不得已寫了個很挫的方法構造。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int p=65521;
const int inf=1e9;
int m;
struct matrix{
	ll a[55][55];
	matrix(){
		memset(a,0,sizeof(a));
	}
	matrix operator * (const matrix &b)const{
		static matrix ans;
		memset(ans.a,0,sizeof(ans.a));
		for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++)
		for(int k=1;k<=m;k++)
		ans.a[i][j]+=a[i][k]*b.a[k][j];
		for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++)
		ans.a[i][j]%=p;
		return ans;
	}
	matrix operator ^ (const ll &k)const{
		static matrix ans,b;
		memcpy(b.a,a,sizeof(a));
		for(int i=1;i<=m;i++)
		ans.a[i][i]=1;
		ll h=k;
		for(;h;h>>=1,b=b*b)if(h&1)ans=ans*b;
		return ans;
	}
};
int pa[6],s[55][6];
int k;
void dfs(int i,int tot){
	if(i>k){
		m++;
		memcpy(s[m],pa,sizeof(pa));
	}else{
		for(int j=1;j<=tot;j++){
			pa[i]=j;
			dfs(i+1,tot);
		}
		pa[i]=tot+1;dfs(i+1,tot+1);
	}
}
int size(int i,int j){
	int sz=0;
	for(int t=1;t<=k;t++)
	sz+=s[i][t]==j;
	return sz;
}
bool same(int i,int j,int k){
	return s[k][i]==s[k][j];
}
int mul(int a,int b){
	int ans=1;
	for(int i=1;i<=b;i++)ans*=a; 
	return ans;
}
int size(int i){
	int ans=0;
	for(int j=1;j<=k;j++)
	ans=max(ans,s[i][j]);
	return ans;
}
int calc(int i){
	int sz=size(i),ans=1;
	for(int j=1;j<=sz;j++)
	ans=ans*mul(size(i,j),size(i,j)-2);
	return ans;
}
bool check(int pa1[6],int pa2[6]){
	for(int i=1;i<=k;i++)
	if(pa1[i]!=pa2[i])return false;
	return true;
}
int find(int pa[6]){
	for(int i=1;i<=m;i++)
	if(check(pa,s[i]))return i;
}
bool check(int s,int t){
	for(int i=1;i<=k;i++)
	for(int j=i+1;j<=k;j++)
	if(same(i,j,t)&&(s&(1<<i-1))&&(s&(1<<j-1)))return false;
	return true;
}
matrix trans;
ll f[55];
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	ll n;scanf("%d%lld",&k,&n);
	dfs(1,0);
	for(int i=1;i<=m;i++){
		int pa[6];
		for(int state=0;state<(1<<k);state++){
			if(size(i,1)==1&&(!(state&(1<<0))))continue;
			if(!check(state,i))continue;
			memset(pa,0,sizeof(pa));
			int g[7][7];memset(g,0,sizeof(g));
			for(int j=1;j<=k;j++)
			for(int h=j+1;h<=k;h++)
			if(same(j,h,i))
			g[j][h]=g[h][j]=1;
			for(int j=1;j<=k;j++)
			if(state&(1<<j-1))g[k+1][j]=g[j][k+1]=1;
			for(int c=1;c<=k+1;c++)
			for(int a=1;a<=k+1;a++)
			for(int b=1;b<=k+1;b++)
			if(g[a][c]&&g[c][b])g[a][b]=1;
			int tot=0;
			for(int j=2;j<=k+1;j++){
				int h;
				for(h=2;h<j;h++)
				if(g[j][h])break;
				if(j==h)pa[j-1]=++tot;
				else pa[j-1]=pa[h-1];
			}
			int t=find(pa);
			trans.a[i][t]++;
		}
	}
	for(int i=1;i<=m;i++)f[i]=calc(i);
	trans=trans^(n-k);
	ll ans=0;
	for(int i=1;i<=m;i++)
	ans=(ans+f[i]*trans.a[i][1])%p;
	printf("%lld\n",ans);
	return 0;
}
		
	


相關推薦

bzoj 1494: [NOI2007]成樹計數

55555555555555555555555555555555被虐哭了 調了兩個小時才調出來,簡直不要太坑。 論文還是比較良心的,就是初始矩陣好難構造的說。 最後迫不得已寫了個很挫的方法構造。 #include<iostream> #include<cs

BZOJ 1494 NOI2007 成樹計數 狀壓DP+矩陣乘法

題目大意:給定n(n≤1015)個點,編號差不超過k(k≤5)的點之間有連邊,問生成樹個數 將k個點的連通性用最小表示法壓成狀態,那麼最多有52種狀態 計算出每個狀態的生成樹個數,作為初始行向量A 對於每種狀態考慮新加入一個點並向這k個點連邊,每種連法可以

BZOJ1494: [NOI2007]成樹計數(Berlekamp-Massey演算法)

傳送門 題解: 直接打表+BM算出遞推式,BM具體實現可以戳這裡 附上一份其醜無比的BM程式碼: const int L=4e2; namespace bm { int cnt,a[N],fail[N],delta[N]; vector <int> R[N]

NOI2007成樹計數 狀壓DP+矩陣乘法

#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int mat[3300]

bzoj1494: [NOI2007]成樹計數

傳送門 將k個點的連通性用最小表示法壓成狀態,那麼最多有52種狀態 最小表示法中,f[i]表示最小的與其聯通的點編號。 計算出每個狀態的生成樹個數,作為初始行向量A 對於每種狀態考慮新加入一個點並向這k個點連邊,每種連法可以轉移到哪些狀態,得到轉移矩陣B

成樹計數 NOI2007

這題一定要把狀態認識清楚,因為只選最後K個點的連通性作為狀態,所以一個狀態可能會對應很多的連邊的情況,由於無法找到特殊的狀態吧初始情況地推出來,所以初始情況需要暴力求解,然後再用矩陣加速。 #include <iostream> #include <cs

【BZOJ1494】【NOI2007成樹計數

【題目連結】 【思路要點】 寫個矩陣樹定理的暴力,求出對於輸入的kk,NN在100以內的結果。 執行BM演算法,我們發現答案是一個至多46階的線性遞推。 然後求它的第NN項就好了。 時間複雜度O(R4+R2LogN)O(R4+R2

淺談成樹計數問題,以SPOJ HIGH, BZOJ 4894, BZOJ 1016為例

給定一個包含n個節點,m條邊的無向圖,問圖中的生成樹的種類數有多少。 點我,喵=w=? 這就是一個最基本的生成樹問題,由此我們可以引出生成樹計數的矩陣樹定理。 矩陣樹定理: 一個無向圖G的生成樹的個數為其基爾霍夫矩陣的任意n-1階主子式的行列式的

BZOJ 1016 成樹計數

zoj 現在 pla class () 技術 main style cmp   現在給出了一個簡單無向加權圖。你不滿足於求出這個圖的最小生成樹,而希望知道這個圖中有多少個不同的最小生成樹。(如果兩顆最小生成樹中至少有一條邊不同,則這兩個最小生成樹就是不同的)。由於不同的最小

【BZOJ1494】【NOI2007成樹計數(動態規劃,矩陣快速冪)

題面 Description 最近,小棟在無向連通圖的生成樹個數計算方面有了驚人的進展,他發現: ·n個結點的環的生成樹個數為n。 ·n個結點的完全圖的生成樹個數為n^(n-2)。這兩個發現讓小棟欣喜若狂,由此更加堅定了他繼續計算生成樹個數的 想法,他

uva10766成樹計數

als mes art 算子 技術分享 math 個數 main mat 此類題是給定一個無向圖,求所有生成樹的個數,生成樹計數要用到Matrix-Tree定理(Kirchhoff矩陣-樹定理) G的度數矩陣D[G]是一個n*n的矩陣,並且滿足:當i≠j時,dij=0;當i

hdu4305成樹計數

open assert with for def com false tor == 先預處理出距離,然後判斷是否可行,要註意判斷是否在一條直線上時判斷是在兩側還是一邊(wa了四次) double型數據 #include<map> #include<se

BZOJ 1016--最小生成樹計數(深搜&kruskal)

names 連通性 如果 int 沒有 計數 ++ struct include     想我這樣的zz根本不會矩陣樹。。。。。 題目鏈接:     http://www.lydsy.com/JudgeOnline/problem.php?id=1016 S

成樹計數及應用 Matrix-Tree

log blog 生成樹計數 mathjax 插值 tps 生成樹 www. 應用 例:給定一個圖,圖上每條邊是紅色或藍色,求恰有 k 條紅邊的生成樹個數. n≤50. Matrix-Tree定理,對於限制條件可以利用多項式,把紅邊邊權設為 X,藍邊為1. 最後求行列式得到

【LOJ】#2320. 「清華集訓 2017」成樹計數

rac res 然而 除了 加法 wap OS 代碼 reg 題解 我,理解題解,用了一天 我,卡常數,又用了一天 到了最後,我才發現,我有個加法取模,寫的是while(c >= MOD) c -= MOD 我把while改成if,時間,少了 六倍。 六倍。 六倍!!

[BZOJ1494]成樹計數

cto operator 個數 最後一行 判斷 state for break desc [BZOJ1494] [NOI2007]生成樹計數 Description 最近,小棟在無向連通圖的生成樹個數計算方面有了驚人的進展,他發現:·n個結點的環的生成樹個數為n。·n個結點

bzoj1494 成樹計數 (dp+矩陣快速冪)

sets 增加 set 基本 表示 2種 least 欺詐 main 題面欺詐系列... 因為一個點最多只能連到前k個點,所以只有當前的連續k個點的連通情況是對接下來的求解有用的 那麽就可以計算k個點的所有連通情況,dfs以下發現k=5的時候有52種。 我們把它們用類似於並

BZOJ 1016 最小生成樹計數

題目連結 https://www.lydsy.com/JudgeOnline/problem.php?id=1016 相關部落格 https://blog.csdn.net/sdfzyhx/article/details/52075151 kur產生的最小生成樹只是一組解,但不一定是唯一的解。 (具

[成樹計數]

prufer序列 每個prufer序列對應一棵唯一的樹。 生成:得到一棵樹的prufer序列的方法是依次去掉編號最小的葉子節點(也就是度數為1的點),然後將這個點的父親加入佇列。直到剩下最後兩個點。這樣就可以得到一個長度為n的prufer序列。 根據prufer序列的生成方式可以得到:每個節點會在pru

1627 Join 成樹計數模板題

                                                                                   URAL - 1627   Join Businessman Petya recently bought a