bzoj1494 Noi2007 生成樹計數

2022-04-30 05:12:09 字數 1727 閱讀 2811

題意:

sol  :前排膜拜

雖然dalao們說了一些最小表示法啊什麼k=5時只有52種狀態啊balabala,然而我並不會.......

因為k很小,可以考慮用狀壓來記錄聯通塊資訊,考慮dp

dp[i][j]表示前i個點,最後k個點聯通情況為j時的方案數

由於n很大,考慮採用矩陣快速冪優化,用並查集判環&維護

那麼最終可以建構函式f[x][y]表示狀態x向狀態y轉移有多少種合法的連線方式

初始函式g[x]是乙個行向量,每個位置代表乙個初始狀態

這樣就可以做了........最後輸出a[1][1]即可

#include#include

#include

#include

#define ll long long

using

namespace

std;

const

int mx=200

;const

int p=65521

;using

namespace

std;

ll n;

int k,tot/*

狀態總數

*/,tr_siz=/*

完全圖的生成樹個數*/;

int fa[mx],siz[mx]/*

第i個聯通塊的大小

*/,status[600],hash[1

<<16]/*

hash[s]=狀態s的編號*/;

struct

matrix

}a,trans;

matrix

operator*(matrix a,matrix b)

void

pow(ll c)

}void dfs(int x,int sta) //

當前要加入第x個點的聯通狀態,當前的狀態為sta

int tmp=-1; //

聯通塊的最大編號,聯通塊編號的區間是[0,k-1]

for(int i=1;i//

!!!當前的sta裡只儲存了1~pos-1這些點的連通性

tmp=max(tmp,sta>>((i-1)*3)&7

);

for(int i=0;i<=tmp+1&&i)

dfs(x+1,sta<<3|i);

}int find(int

x) int get_status() //

用當前的並查集來求出新的點2到點k+1的最小表示

return

hash[sta];

}void cal(int sta,int addsta) //

用加邊狀態addsta去更新最小表示法sta,addsta裡的第i位為1表示第k+1個點要和點i+1連新邊

for(int i=1;i<=k;i++)

if(addsta&(1

<<(i-1

)))

bool flag=false; //

flag=true表示有點和點1聯通

for(int i=2;i<=k+1;i++)

if(find(i)==find(1

))

if(!flag) return; //

點1不鏈結後面的點,那麼這個生成樹不聯通

trans.num[sta][get_status()]++;

}int

main()

BZOJ 1491 NOI2007 社交網路

顯然這是一道要求多源最短路的題目,資料範圍很小,目測用弗洛伊德演算法。由題意,先求出各個點之間的最短路徑,同時利用乘法原理,計算出由 i 到 j 之間的最短路徑個數。如果又發現了一條最短路,由乘法原理計算增加的路徑個數再加上即可。我寫的 沒有去除自己到自己的路徑,因此需清空,但也可在 floyd 中...

BZOJ 1491 NOI2007 社交網路

傳送門 資料範圍很小,我們考慮floyd。要求的是路徑條數,所以我們在floyd的時候直接預處理出兩點之間的路徑條數。用num i j 表示,然後floyd的時候,如果dis i j include include include using namespace std const int maxn...

BZOJ 1491 NOI2007 社交網路

鏈結 我是鏈結,點我呀 題意 在這裡輸入題意 題解 floyd演算法 算出任意兩點之間的最短路,以及最短路的條數。然後三重迴圈列舉v,s,t就好 看看s到t的最短路徑不經過v 經過的話增加答案貢獻 o n 3 include include define ll long long using nam...