【BZOJ2525】[Poi2011]Dynamite 二分+樹形DP
阿新 • • 發佈:2017-11-19
接下來 family string style tput include n-1 poi mic
1 0 1 1 0 1 1
1 3
2 3
3 4
4 5
5 6
5 7
【BZOJ2525】[Poi2011]Dynamite
Description
Byteotian Cave的結構是一棵N個節點的樹,其中某些點上面已經安置了炸.藥,現在需要點燃M個點上的引線引爆所有的炸.藥。某個點上的引線被點燃後的1單位時間內,在樹上和它相鄰的點的引線會被點燃。如果一個有炸.藥的點的引信被點燃,那麽這個點上的炸.藥會爆炸。 求引爆所有炸.藥的最短時間。 輸入: 第一行是兩個整數N,M。(1<=m<=n<=300000) 接下來一行有N個整數Di,第I個數為1表示該點有炸.藥。 接下來N-1行每行有兩個數A,B,表示A和B之間有一條邊。 輸出: 最短時間。 樣例解釋: 點燃3,5上的引線。
Sample Input
7 21 0 1 1 0 1 1
1 3
2 3
3 4
4 5
5 6
5 7
Sample Output
1題解:一眼想到二分+貪心,但是細節還是極其多的~
首先二分時間limit,然後用f[i]表示i子樹中,最少要點燃多少引線。但是i子樹中的部分炸.藥可能由i子樹外的點引燃,所以設g[i]表示在f[i]最小的前提下,最少有多少層還沒有被點燃;i子樹中的引線也可能引燃子樹外的炸.藥,所以h[i]表示在f[i]最小的前提下,最多還能點燃子樹外的多少層炸.藥。顯然g[x]和h[x]同時只能存在一個。
轉移時細節挺多的,見代碼吧~
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=300010; int n,m,cnt,mid; int to[maxn<<1],next[maxn<<1],head[maxn],d[maxn],f[maxn],g[maxn],h[maxn]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+(gc^‘0‘),gc=getchar(); return ret*f; } inline void add(int a,int b) { to[cnt]=b,next[cnt]=head[a],head[a]=cnt++; } void dfs(int x,int fa) { int i,y; if(d[x]) g[x]=0,h[x]=-1<<20; else g[x]=h[x]=-1<<20; f[x]=0; for(i=head[x];i!=-1;i=next[i]) if(to[i]!=fa) { y=to[i],dfs(y,x),f[x]+=f[y],g[x]=max(g[x],g[y]+1),h[x]=max(h[x],h[y]-1); } if(h[x]>=g[x]) g[x]=-1<<20; else if(g[x]<mid) h[x]=-1<<20; else f[x]++,g[x]=-1<<20,h[x]=mid; } int main() { n=rd(),m=rd(); int i,a,b,l=0,r=n; memset(head,-1,sizeof(head)); for(i=1;i<=n;i++) d[i]=rd(); for(i=1;i<n;i++) a=rd(),b=rd(),add(a,b),add(b,a); while(l<r) { mid=(l+r)>>1,dfs(1,0); if(g[1]>=0) f[1]++; if(f[1]<=m) r=mid; else l=mid+1; } printf("%d",r); return 0; }//5 0 0 0 0 0 0 1 2 2 3 3 4 4 5
【BZOJ2525】[Poi2011]Dynamite 二分+樹形DP