生成樹計數 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可以忽略掉,所以他的範圍完全是嚇唬人的。 題解: 抱歉,這道題我真的無法弄的通俗的說出來