1. 程式人生 > >生成樹計數 NOI2007

生成樹計數 NOI2007

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

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>   
#include <map>
#include <string>  
#include <climits> 
#include <set>
#include <string>    
#include <sstream>
#include <utility>   
#include <ctime>

using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::stringstream;
using std::make_pair;
using std::getline;
using std::greater;
using std::endl;
using std::multimap;
using std::deque;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PAIR;
typedef multimap<int, int> MMAP;

const int MAXN(6010);
const int MAXM(5010);
const int MAXE(10010);
const int HSIZE(13131);
const int SIGMA_SIZE(26);
const int MAXH(19);
const int INFI((INT_MAX-1) >> 1);
const ULL BASE(31);
const LL LIM(10000000);
const int INV(-10000);
const int MOD(65521);

template<typename T>
inline T ABS(const T &op){return op < 0? -op: op;}

LL gcd(LL a, LL b)
{
	LL temp;
	while(b)
	{
		temp = a%b;
		a = b;
		b = temp;
	}
	return a;
}
LL lcm(LL a, LL b){return a/gcd(a, b)*b;}

struct MAT
{
	int r, c;
	LL arr[60][60];
	MAT(int tr, int tc): r(tr), c(tc)
	{
		memset(arr, 0, sizeof(arr[0])*r);
	}
	MAT()
	{}
	void reset()
	{
		memset(arr, 0, sizeof(arr[0])*r);
	}
	void operator = (const MAT &op)
	{
		r = op.r;
		c = op.c;
		memcpy(arr, op.arr, sizeof(arr[0])*r);
	}
	void identity()
	{
		reset();
		for(int i = 0; i < r; ++i)
			arr[i][i] = 1;
	}
};

void mat_mul(const MAT &op1, const MAT &op2, MAT &re)
{
	re.r = op1.r;
	re.c = op2.c;
	re.reset();
	for(int i = 0; i < op1.c; ++i)
		for(int j = 0; j < op1.r; ++j)
			for(int k = 0; k < op2.c; ++k)
				re.arr[j][k] = (re.arr[j][k]+op1.arr[j][i]*op2.arr[i][k])%MOD;
}

MAT t1, t2;

void mat_pow(const MAT &op, LL n, MAT &re)
{
	t1 = op;
	re.r = op.r;
	re.c = op.c;
	re.identity();
	for(int i = 0; (1LL << i) <= n; ++i)
	{
		if(n&(1LL << i))
		{
			t2 = re;
			mat_mul(t1, t2, re);
		}
		mat_mul(t1, t1, t2);
		t1 = t2;
	}
}

MAT mat, re, tmat;

struct HASH_MAP
{
	int first[HSIZE], next[MAXN];
	int state[MAXN];
	int value[MAXN];
	int size;
	void init()
	{
		memset(first, -1, sizeof(first));
		size = 0;
	}
	int insert(int ts, int tv)
	{
		int h = ts%HSIZE;
		for(int i = first[h]; ~i; i = next[i])
			if(state[i] == ts)
			{
				value[i] += tv;
				return i;
			}
		value[size] = tv;
		state[size] = ts;
		next[size] = first[h];
		first[h] = size;
		return size++;
	}
} hm1, hm2;

int K;
int tcode[5], code[5], Num[8];

void decode(int ts)
{
	for(int i = 0; i < K; ++i)
	{
		tcode[i] = ts&7;
		ts >>= 3;
	}
}

int encode()
{
	int ret = 0, cnt = 0;
	memset(Num, -1, sizeof(Num));
	for(int i = K-1; i >= 0; --i)
	{
		if(Num[code[i]] == -1) Num[code[i]] = cnt++;
		ret = (ret << 3)|Num[code[i]];
	}
	return ret;
}

bool vis[5];

void updata()
{
	int cnt = 0;
	for(int i = 0; i < K; ++i)
		if(tcode[i] == tcode[0]) ++cnt;
	int lim = (1 << K)-1;
	for(int i = 0; i <= lim; ++i)
	{
		if(cnt == 1 && (i&1) == 0) continue;
		memset(vis, 0, sizeof(vis));
		bool flag(true);
		int num = 7;
		for(int j = 0; j < K; ++j)
			if(i&(1 << j))
			{
				if(vis[tcode[j]])
				{
					flag = false;
					break;
				}
				num = tcode[j];
				vis[tcode[j]] = true;
			}
		if(flag)
		{
			for(int j = 1; j < K; ++j)
				code[j-1] = tcode[j];
			for(int j = 0; j < K-1; ++j)
				if(vis[code[j]])
					code[j] = num;
			code[K-1] = num;
			hm2.insert(encode(), 1);
		}
	}
}

void process(int ind)
{
	hm2.init();
	decode(hm1.state[ind]);
	updata();
	for(int i = 0; i < hm2.size; ++i)
	{
		int temp = hm1.insert(hm2.state[i], 0);
		mat.arr[ind][temp] = hm2.value[i];
	}
}

void dfs(int dep)
{
	if(dep == K)
	{
		hm1.insert(encode(), 0);
		return;
	}
	for(int i = 0; i <= dep; ++i)
	{
		code[dep] = i;
		dfs(dep+1);
	}
}

struct FIND_SET
{
	int fa[5];
	void init()
	{
		for(int i = 0; i < 5; ++i)
			fa[i] = i;
	}
	int find(int sour){return sour == fa[sour]? sour: fa[sour] = find(fa[sour]);}
	bool Union(int a, int b)
	{
		a = find(a);
		b = find(b);
		if(a == b) return false;
		fa[b] = a;
		return true;
	}
} fs;

int conn[5];
bool closure[5][5];

void dfs2(int dep)   //求解初始狀態
{
	if(dep == K)
	{
		fs.init();
		memset(closure, 0, sizeof(closure));
		for(int i = 0; i < K; ++i)
		{
			for(int j = i+1; j < K; ++j)
				if(conn[i]&(1 << (K-1-j)))
				{
					if(!fs.Union(i, j)) return;
					closure[i][j] = true;
					closure[j][i] = true;
				}
		}
		for(int i = 0; i < K; ++i)
			for(int j = 0; j < K; ++j)
				for(int k = 0; k < K; ++k)
					closure[j][k] |= closure[j][i]&closure[i][k];
		int cnt = 0;
		memset(code, -1, sizeof(code));
		for(int i = 0; i < K; ++i)
		{
			if(code[i] == -1) 
				code[i] = cnt++;
			else
				continue;
			for(int j = i+1; j < K; ++j)
				if(closure[i][j])
					code[j] = code[i];
		}
		int temp = hm1.insert(encode(), 0);
		tmat.arr[0][temp] += 1;
		return;
	}
	int lim = (1 << (K-1-dep))-1;
	for(int i = 0; i <= lim; ++i)
	{
		conn[dep] = i;
		dfs2(dep+1);
	}
}


void solve(LL n)
{
	hm1.init();
	dfs(0);
	tmat.r = 1;
	tmat.c = hm1.size;
	for(int i = 0; i < hm1.size; ++i)
		tmat.arr[0][i] = 0;
	dfs2(0);
	mat.r = mat.c = hm1.size;
	mat.reset();
	for(int i = 0; i < hm1.size; ++i)
		process(i);
	mat_pow(mat, n-K, re);
	mat_mul(tmat, re, mat);
	printf("%lld\n", mat.arr[0][0]);
}

int main()
{
	LL n;
	while(~scanf("%d%lld", &K, &n))
	{
		solve(n);
	}
	return 0;
}


相關推薦

成樹計數 NOI2007

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

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]

bzoj 1494: [NOI2007]成樹計數

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

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

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

【BZOJ1494】【NOI2007成樹計數

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

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

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

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

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

bzoj1494: [NOI2007]成樹計數

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

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

成樹計數及應用 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種。 我們把它們用類似於並

[成樹計數]

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

1627 Join 成樹計數模板題

                                                                                   URAL - 1627   Join Businessman Petya recently bought a

清華集訓2017 成樹計數

題意: 給定 n n n個連通塊,每個連通塊的大小為

hdu4305Lightning 成樹計數(基爾霍夫矩陣)+高斯消元+逆元

題意:比較裸的生成樹計數問題。   如何處理生成樹計數問題? 基爾霍夫矩陣: if i==j  Kir[i][j] = i的度數 if i!=j   Kir[i][j] = i到j的平行邊的個數的負數 即,基爾霍夫矩陣 = 度數矩陣 - 鄰接矩陣 將基爾霍夫矩陣刪去第i

生成成樹計數 --- Matrix-Tree定理(基爾霍夫矩陣樹定理)

模板題點這 題目大意: *一個有n座城市的組成國家,城市1至n編號,其中一些城市之間可以修建高速公路; *需要有選擇的修建一些高速公路,從而組成一個交通網路; *計算有多少種方案

kuangbin專題八 UVA10766 (成樹計數)Organising the Organisation(請無視這篇文章)

題意: 給出n,m,k,代表一家公司有n個部門,編號1到n,有m組關係,表示i和j不能直接聯通,k代表主管部門,問你有多少種分層方案。另外,這道題的k可以忽略掉,所以他的範圍完全是嚇唬人的。 題解: 抱歉,這道題我真的無法弄的通俗的說出來