洛谷題目傳送門c++
費了幾個小時槓掉此題,若是不是那水水的數據的話,跟列隊的難度真的是有得一比。。。spa
話說蒟蒻仔細翻了全部的題解,發現巨佬寫的都是倍增,複雜度是\(O(n\log n\log nw)\)的,貌似還不夠優秀。code
其實咱們與其對於每個點都經過倍增向上找到對應位置,還不如直接從上到下dfs一遍,判斷:若是當前點子樹內初始位置最淺的軍隊與當前點距離不超過\(mid\),或者全部子樹都被封鎖,那麼當前點也被封鎖。排序
這樣之後再二分,時間複雜度降至\(O(n\log nw)\)。其它部分的思路Dalao們的題解裏都講清楚了,蒟蒻也很少說了qwqci
#include<bits/stdc++.h> #define LL long long #define RG register #define R RG int using namespace std; const int N=5e4+9,M=1e5+9; int p,he[N],ne[M],to[M],w[M],top[N],at[N],t[N],mn[N]; LL mid,d[N],dis[N]; bool cov[N],use[N]; inline bool cmp(R x,R y){ return d[x]>d[y]; } void dp(R x,R f){ top[x]=p; if(mid<d[x])mid=d[x];//控制二分上界 if(to[he[x]]==f)he[x]=ne[he[x]];//悄悄把反邊刪掉 for(R i=he[x];i;i=ne[i]){ if(to[ne[i]]==f)ne[i]=ne[ne[i]]; d[to[i]]=d[x]+w[i]; dp(to[i],x); } } void dfs(R x){ if(cov[x]){dis[x]=0;return;} dis[x]=1ll<<60; if(!(cov[x]=he[x]))return;//到達葉子節點尚未被封鎖 for(R y,i=he[x];i;i=ne[i]){ dfs(y=to[i]); cov[x]&=cov[y]; dis[x]=min(dis[x],dis[y]+w[i]); } if(dis[x]<=mid)cov[x]=1;//子樹內軍隊可以趕到 } int main(){ R n,m,cnt=0,i,u,v; cin>>n; for(i=1;i<n;++i){ cin>>u>>v>>w[++p];w[p+1]=w[p]; ne[p]=he[u];to[he[u]=p]=v;++p; ne[p]=he[v];to[he[v]=p]=u; } for(i=he[1];i;i=ne[i]) d[p=to[i]]=w[i],dp(t[++cnt]=to[i],1); cin>>m; if(cnt>m)return cout<<"-1"<<endl,0;//無解 for(i=1;i<=m;++i)cin>>at[i]; sort(t+1,t+cnt+1,cmp);//排序,方便接下來貪心 sort(at+1,at+m+1,cmp); RG LL l=0,r=mid+d[t[1]]; while(l<r){ mid=(l+r)>>1; memset(cov,0,n+1); memset(use,0,n+1); for(i=1;i<=cnt;++i)mn[t[i]]=0;//清空 for(i=1;d[at[i]]>mid;++i) cov[at[i]]=1;//到不了根節點,直接留在子樹內 for(p=i;i<=m;++i) if(!mn[top[at[i]]])mn[top[at[i]]]=i; //每一個子樹預留一個貢獻最小的軍隊 use[0]=1;u=m+1; for(i=1;i<=cnt;++i){ dfs(t[i]); if(cov[t[i]])continue; if(use[mn[t[i]]]){//預留已用,只好拿其它子樹的 for(--u;u>=p&&(d[at[u]]+d[t[i]]>mid||use[u]);--u); if(u<p)break; use[u]=1; } else use[mn[t[i]]]=1;//預留直接用 } u>=p?r=mid:l=mid+1; } cout<<l<<endl; return 0; }