noip2012-疫情控制(倍增)
阿新 • • 發佈:2018-11-06
啊我又來寫這題惹。
還是一樣的二分limit emmmm
然後知道了limit判斷行不行(ok函式)
先把點都往上提。分成兩類點:1.在limit範圍內提得到根節點的。2.在limit範圍內提不到根節點的。
(這裡用倍增上提以優化複雜度,不贅述了)
提不到根節點的肯定是讓他留在離根最近的點那邊啊(毋庸置疑誒)
然後我們用vis給它標記一下,意思是vis以下的結點都ok的
而對於提的到根節點的軍隊,我們存一下它的結點號和到了根節點之後還剩下的距離(a陣列)
現在我們要檢查一下提完後還有啥路徑沒有被覆蓋到的,同樣存一下節點號和到根的距離(b陣列)注意對於每條路徑這裡只要找離根只有一條路的距離的點(顯然這些點才是最優的)
如果check完了發現所有路徑全被覆蓋了,直接return 1
否則我們繼續……
下面就是結點和軍隊的配對咯。
剛才忘說了,對於找到的每一個到的了根節點的點,我們倍增完了順便用他跟新一下結點x的restmin(說白了就是和它在一條路徑上就短的軍隊)
sort一下(大的配對大的,小的配對小的)emmmm這裡的sort一定要從大到小不然只有80分。。。因為一定要先滿足遠的點再滿足近的點啊。。。否則不是最優
一個個看b點,先找有沒有和它一個路徑且沒用過的點,如果有直接標註用過並continue
不然將a陣列的軍隊一個個掃描,看有木有能匹配的。如果沒有直接返回0
全能匹配就check return 1啦!
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int maxn=500005; int cnt=0;int n,m,na,nb; int u[maxn*2],v[maxn*2],w[maxn*2],head[maxn],nxt[maxn*2]; int p[maxn][35];ll dis[maxn][35];int army[maxn],used[maxn],vis[maxn]; int rest[maxn],restmin[maxn]; struct node { int id,rest; }a[maxn],b[maxn];//a是軍隊,b是點 void add_edge(int x,int y,int z) { cnt++;u[cnt]=x;v[cnt]=y;w[cnt]=z; nxt[cnt]=head[x];head[x]=cnt; } int cmp(node x,node y) { return x.rest>y.rest; } void find(int x,int fa) { p[x][0]=fa; for(int i=head[x];i!=-1;i=nxt[i]) { if(v[i]!=fa) { dis[v[i]][0]=w[i]; find(v[i],x); } } } void prework() { for(int j=1;j<=17;j++) { for(int i=1;i<=n;i++) { p[i][j]=p[p[i][j-1]][j-1]; dis[i][j]=dis[i][j-1]+dis[p[i][j-1]][j-1]; } } } int checkok(int x,int fa) { int fflag=0,flag=1; if(vis[x]) return 1; for(int i=head[x];i!=-1;i=nxt[i]) { if(v[i]==fa) continue;fflag=1; if(!checkok(v[i],x)) { flag=0; if(x==1) {b[++nb].id=v[i];b[nb].rest=w[i];}//b是要匹配的點 else return 0; } } if(!fflag) return 0; return flag; } int ok(int limit) { na=nb=0; for(int i=1;i<=n;i++) vis[i]=rest[i]=0; for(int i=1;i<=m;i++) used[i]=0; for(int i=1;i<=m;i++) { int x=army[i],num=0; for(int j=17;j>=0;j--) { if(p[x][j]>1&&(num+dis[x][j])<=limit) { num+=dis[x][j];x=p[x][j]; } } if(p[x][0]==1&&num+dis[x][0]<=limit) { a[++na].rest=limit-num-dis[x][0];a[na].id=i; if(!rest[x]||a[na].rest<restmin[x]) restmin[x]=a[na].rest,rest[x]=i; } else vis[x]=1; } if(checkok(1,0)) return 1; sort(a+1,a+1+na,cmp);sort(b+1,b+1+nb,cmp); int now=1;used[0]=1; for(int i=1;i<=nb;i++) { if(!used[rest[b[i].id]]) {used[rest[b[i].id]]=1;continue;} while(now<=na&&(used[a[now].id]||a[now].rest<b[i].rest)) ++now;//找匹配 if(now>na) return 0;used[a[now].id]=1; } return 1; } int main() { memset(head,-1,sizeof(head)); scanf("%d",&n); for(int i=1;i<n;i++) { int x,y,z;scanf("%d%d%d",&x,&y,&z); add_edge(x,y,z);add_edge(y,x,z); } find(1,0);prework(); scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&army[i]); int l=0,r=500001;int ans=-1; while(l<=r) { int mid=(l+r)/2; if(ok(mid)) {r=mid-1;ans=mid;} else l=mid+1; } printf("%d\n",ans); return 0; }