1. 程式人生 > >【NOIP】提高組2012 疫情控制

【NOIP】提高組2012 疫情控制

closed ide span sin 容易 lld isdigit i++ 表示

【題意】n個點的樹,1為根,要求刪除一些點使得截斷根節點和所有葉子結點的路徑(不能刪根,可以刪葉子)。有m支軍隊在m個點上,每時刻所有軍隊可以走一步,最終走到的地方就是刪除的點,求最短時間。

【題解】

所有點同時走路,求最短時間,這樣的詢問通常考慮二分轉化為判定性問題。(實際上,這題用二分確實沒有想到,如果能想到二分整道題就好寫一些了)

容易發現,每支軍隊貪心地往上走最優。

那麽對於二分的時間,有一部分軍隊可以到達根,A數組記錄這些軍隊到達根後的剩余時間,待會可以走到第二層覆蓋其它節點。

有一部分軍隊不能到達根,處理出這些軍隊能覆蓋多少二層節點,B數組不能覆蓋的二層節點到根的路徑。

AB各自排序之後,對應匹配,若A能將B全部匹配就可以滿足要求,否則不能。

還有一個問題,一個軍隊雖然不能到達根後返回來覆蓋自己,但可以直接不去根。解決方法是從小到大枚舉A時,如果該點本身的二層節點還沒覆蓋就直接覆蓋(因為該點本來就是最劣的,只要能覆蓋一個二層結點就不虧)。

最後的問題是處理出不能到達根的軍隊能覆蓋多少二層結點?可以對每個軍隊倍增,也可以直接一遍dfs。

dfs的具體做法是:c[x]表示x被覆蓋,t[x]表示x子樹的軍隊到x的最大剩余時間。c[x] = c[son[x]]=1 || t[x]>=0。son[x]表示x的所有兒子。

技術分享
#include<cstdio>
#include<cstring>
#include<cctype>
#include
<algorithm> #define ll long long using namespace std; int read(){ char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c==-)t=-1; do{s=s*10+c-0;}while(isdigit(c=getchar())); return s*t; } const int maxn=100010; int n,m,tot,first[maxn],top[maxn],a[maxn]; ll dis[maxn],t[maxn]; bool b[maxn],c[maxn];
struct edge{int v,w,from;}e[maxn*2]; void insert(int u,int v,int w){tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;} void DFS(int x,int fa,int tp){ top[x]=tp; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ dis[e[i].v]=dis[x]+e[i].w;//yu ju shun xu DFS(e[i].v,x,tp); } } void dfs(int x,int fa){ c[x]=0; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){c[x]=1;break;} for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ dfs(e[i].v,x); c[x]&=c[e[i].v]; t[x]=max(t[x],t[e[i].v]-e[i].w); } if(t[x]>=0)c[x]=1; } int totA,totB; struct cyc{ll num;int id;}A[maxn],B[maxn];// bool cmp(cyc a,cyc b){return a.num<b.num;} bool check(ll time){ memset(t,-1,sizeof(t)); totA=0;totB=0; for(int i=1;i<=m;i++)if(dis[a[i]]<=time)b[i]=1,A[++totA].num=time-dis[a[i]],A[totA].id=top[a[i]]; else b[i]=0,t[a[i]]=time; dfs(1,0); for(int i=first[1];i;i=e[i].from)if(!c[e[i].v])B[++totB].num=e[i].w,B[totB].id=e[i].v; sort(A+1,A+totA+1,cmp);sort(B+1,B+totB+1,cmp); int now=1; for(int i=1;i<=totA;i++){ while(now<=totB&&c[B[now].id])now++; if(!c[A[i].id]){c[A[i].id]=1;continue;} if(now<=totB&&A[i].num>=B[now].num){c[B[now++].id]=1;} } while(now<=totB&&c[B[now].id])now++;// if(now>=totB+1)return 1; return 0; } int main(){ n=read(); for(int i=1;i<n;i++){ int u=read(),v=read(),w=read(); insert(u,v,w);insert(v,u,w); } memset(dis,0,sizeof(dis)); for(int i=first[1];i;i=e[i].from)dis[e[i].v]=e[i].w,DFS(e[i].v,1,e[i].v); m=read(); for(int i=1;i<=m;i++)a[i]=read(); ll l=0,r=1ll*n*1e9+1,mid; while(l<r){ mid=(l+r)>>1; if(check(mid))r=mid;else l=mid+1; } if(r>1ll*n*1e9)printf("-1");else printf("%lld",l); return 0; }
View Code

【NOIP】提高組2012 疫情控制