省選一試後的第一篇blog!spa
Z國坐落於遙遠而又神奇的東方半島上,在小Z的統治時代,公路成爲這裏主要的交通手段。Z國共有n座城市,一些城市之間由雙向的公路所鏈接。很是神奇的是Z國的每一個城市所處的經度都不相同,而且最多隻和一個位於它東邊的城市直接經過公路相連。Z國的首都是Z國政治經濟文化旅遊的中心,天天都有成千上萬的人從Z國的其餘城市涌向首都。爲了使Z國的交通更加便利順暢,小Z決定在Z國的公路系統中肯定若干條規劃路線,將其中的公路所有改建爲鐵路。咱們定義每條規劃路線爲一個長度大於1的城市序列,每一個城市在該序列中最多出現一次,序列中相鄰的城市之間由公路直接相連(待改建爲鐵路)。而且,每一個城市最多隻能出如今一條規劃路線中,也就是說,任意兩條規劃路線不能有公共部分。固然在通常狀況下是不可能將全部的公路修建爲鐵路的,所以從有些城市出發去往首都依然須要經過乘坐長途汽車,而長途汽車只往返於公路鏈接的相鄰的城市之間,所以從某個城市出發可能須要不斷地換乘長途汽車和火車才能到達首都。咱們定義一個城市的「不便利值」爲從它出發到首都須要乘坐的長途汽車的次數,而Z國的交通系統的「不便利值」爲全部城市的不便利值的最大值,很明顯首都的「不便利值」爲0。小Z想知道如何肯定規劃路線修建鐵路使得Z國的交通系統的「不便利值」最小,以及有多少種不一樣的規劃路線的選擇方案使得「不便利值」達到最小。固然方案總數可能很是大,小Z只關心這個天文數字modQ後的值。注意:規劃路線1-2-3和規劃路線3-2-1是等價的,即將一條規劃路線翻轉依然認爲是等價的。兩個方案不一樣當且僅當其中一個方案中存在一條規劃路線不屬於另外一個方案。設計
第一行包含三個正整數N、M、Q,其中N表示城市個數,M表示公路總數,N個城市從1~N編號,其中編號爲1的是首都。Q表示上文提到的設計路線的方法總數的模數。接下來M行,每行兩個不一樣的正數ai、bi表示有一條公路鏈接城市ai和城市bi。輸入數據保證一條公路只出現一次。3d
包含兩行。第一行爲一個整數,表示最小的「不便利值」。第二行爲一個整數,表示使「不便利值」達到最小時不一樣的設計路線的方法總數modQ的值。若是某個城市沒法到達首都,則輸出兩行-1。code
5 4 100
1 2
4 5
1 3
4 1blog
1
10ip
對於100%的數據,知足1≤N,M≤100000,1≤Q≤120000000,1≤ai,bi≤N。get
題面描述得至關彆扭啊,小C也是看過discuss後才知道它給的是一座森林。string
正常來講學過樹鏈剖分的人只要看到這一條件,題目就已經作完了。it
題目實際上要咱們求的是,求一棵樹的樹鏈剖分方案,使得全部點到根的路徑上所走的輕邊數量的最大值最小。io
學過樹鏈剖分的人都應該知道,若是按照重鏈剖分(即重邊連向結點更多的子樹)來劃分一棵樹,那麼從某個點出發走到根節點最多隻要走logN條重鏈。
樹剖複雜度的證實在此提一下,其實也很簡單,設x個結點組成的樹中,全部點到根路徑上輕邊數量最大值的最大值爲f(x)。
那麼就有,因爲f(x)是遞增的,因此f(x)一定是從f((x-1)/2)+1轉移獲得的。
因此轉移式化簡就是:,很顯然f(N)是logN級別的。
所以答案最大不會超過logN,並且這樣形態的樹是一棵二叉樹。
但實際上這道題所說的鏈剖不是真正意義上的鏈剖,劃分出來的路徑是能夠拐彎的,所以答案會比logN更小一些,實際上在樹的形態是三叉樹的狀況下答案達到最大,大概是log_3(N)級別。
而後知道了以上這些結論咱們能夠很是容易地寫出DP的狀態和轉移方程:
設f[i][j][0/1]表示在結點i的子樹中,答案爲j,且該子樹可否繼續向上連邊的鏈剖方案數。轉移花log^2隨便轉移一下就行。
最後判斷答案的時候簡單地根據該處的DP值是否爲0來取最小值是不可行的,由於方案數極可能對Q取模等於0,因此這裏有一個小技巧,就是方案數爲Q的倍數的時候,將對Q取模的值設爲Q便可。
時間複雜度。
#include <cstdio> #include <algorithm> #include <cstring> #define ll long long #define MN 100005 #define MS 12 using namespace std; struct edge{int nex,to;}e[MN<<1]; int n,m,mod,pin; int hr[MN],f[MN][2][MS]; inline int read() { int n=0,f=1; char c=getchar(); while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();} while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();} return n*f; } inline void rw(int& x,int y) {x+=y; if(x>mod)x-=mod;} inline int modu(ll x) {return !x?0:(x-1)%mod+1;} inline void ins(int x,int y) {e[++pin]=(edge){hr[x],y}; hr[x]=pin;} void dfs(int x,int fat) { register int i,j,k; int g[3][MS],h[3][MS+5]; memset(g,0,sizeof(g)); memset(h,0,sizeof(h)); g[0][0]=1; for (i=hr[x];i;i=e[i].nex) { if (e[i].to==fat) continue; dfs(e[i].to,x); for (j=0;j<MS;++j) for (k=0;k<MS;++k) { rw(h[2][max(j+1,k)],modu(1LL*g[2][k]*(f[e[i].to][0][j]+f[e[i].to][1][j]))); rw(h[2][max(j,k)],modu(1LL*g[1][k]*f[e[i].to][0][j])); rw(h[1][max(j+1,k)],modu(1LL*g[1][k]*(f[e[i].to][0][j]+f[e[i].to][1][j]))); rw(h[1][max(j,k)],modu(1LL*g[0][k]*f[e[i].to][0][j])); rw(h[0][max(j+1,k)],modu(1LL*g[0][k]*(f[e[i].to][0][j]+f[e[i].to][1][j]))); } for (j=0;j<MS;++j) g[0][j]=h[0][j],h[0][j]=0, g[1][j]=h[1][j],h[1][j]=0, g[2][j]=h[2][j],h[2][j]=0; } for (i=0;i<MS;++i) f[x][0][i]=g[0][i],rw(f[x][0][i],g[1][i]),f[x][1][i]=g[2][i]; } int main() { register int i,x,y; n=read(); m=read(); mod=read(); if (m!=n-1) return 0*printf("-1\n-1"); for (i=1;i<=m;++i) x=read(),y=read(), ins(x,y),ins(y,x); dfs(1,0); for (i=0;i<MS;++i) if (f[1][0][i]||f[1][1][i]) return 0*printf("%d\n%d",i,(f[1][0][i]+f[1][1][i])%mod); }
心血來潮寫了一下小D的mint,交上去居然T了,好感度--,表示這模板不是很想再用了。