[BZOJ]1063 道路設計(Noi2008)
省選一試後的第一篇blog!
Description
Z國坐落於遙遠而又神奇的東方半島上,在小Z的統治時代,公路成為這裏主要的交通手段。Z國共有n座城市,一些城市之間由雙向的公路所連接。非常神奇的是Z國的每個城市所處的經度都不相同,並且最多只和一個位於它東邊的城市直接通過公路相連。Z國的首都是Z國政治經濟文化旅遊的中心,每天都有成千上萬的人從Z國的其他城市湧向首都。為了使Z國的交通更加便利順暢,小Z決定在Z國的公路系統中確定若幹條規劃路線,將其中的公路全部改建為鐵路。我們定義每條規劃路線為一個長度大於1的城市序列,每個城市在該序列中最多出現一次,序列中相鄰的城市之間由公路直接相連(待改建為鐵路)。並且,每個城市最多只能出現在一條規劃路線中,也就是說,任意兩條規劃路線不能有公共部分。當然在一般情況下是不可能將所有的公路修建為鐵路的,因此從有些城市出發去往首都依然需要通過乘坐長途汽車,而長途汽車只往返於公路連接的相鄰的城市之間,因此從某個城市出發可能需要不斷地換乘長途汽車和火車才能到達首都。我們定義一個城市的“不便利值”為從它出發到首都需要乘坐的長途汽車的次數,而Z國的交通系統的“不便利值”為所有城市的不便利值的最大值,很明顯首都的“不便利值”為0。小Z想知道如何確定規劃路線修建鐵路使得Z國的交通系統的“不便利值”最小,以及有多少種不同的規劃路線的選擇方案使得“不便利值”達到最小。當然方案總數可能非常大,小Z只關心這個天文數字modQ後的值。註意:規劃路線1-2-3和規劃路線3-2-1是等價的,即將一條規劃路線翻轉依然認為是等價的。兩個方案不同當且僅當其中一個方案中存在一條規劃路線不屬於另一個方案。
Input
第一行包含三個正整數N、M、Q,其中N表示城市個數,M表示公路總數,N個城市從1~N編號,其中編號為1的是首都。Q表示上文提到的設計路線的方法總數的模數。接下來M行,每行兩個不同的正數ai、bi表示有一條公路連接城市ai和城市bi。輸入數據保證一條公路只出現一次。
Output
包含兩行。第一行為一個整數,表示最小的“不便利值”。第二行為一個整數,表示使“不便利值”達到最小時不同的設計路線的方法總數modQ的值。如果某個城市無法到達首都,則輸出兩行-1。
Sample Input
5 4 100
1 2
4 5
4 1
Sample Output
1
10
HINT
對於100%的數據,滿足1≤N,M≤100000,1≤Q≤120000000,1≤ai,bi≤N。
Solution
題面描述得相當別扭啊,小C也是看過discuss後才知道它給的是一座森林。
正常來說學過樹鏈剖分的人只要看到這一條件,題目就已經做完了。
題目實際上要我們求的是,求一棵樹的樹鏈剖分方案,使得所有點到根的路徑上所走的輕邊數量的最大值最小。
學過樹鏈剖分的人都應該知道,如果按照重鏈剖分(即重邊連向結點更多的子樹)來劃分一棵樹,那麽從某個點出發走到根節點最多只要走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); }
Last Word
心血來潮寫了一下小D的mint,交上去竟然T了,好感度--,表示這模板不是很想再用了。
[BZOJ]1063 道路設計(Noi2008)