noip 2015 运输计划 (lca+二分)
/* 95 最后一个点T了 qian lv ji qiong 了 没学过树剖 听chx听xzc说的神奇的方法 Orz 首先求出每个计划的路径长度 这里写的倍增 然后二分答案 对于每个ans 统计>他的路径条数 tot 并维护最大差值 dec 并且对于每条不合法的路径维护每个点的经过次数 然后枚举点 如果经过次数==tot说明每一条不合法的都经过他 然后尝试把它建成虫洞 如果他对应边的权值>=dec 那么我们删掉它ans就合法了 关键是统计每个点在非法路径中的经过次数 : 维护sum数组 对于每个非法的路径起点a b LCA(a,b)==s sum[a]++ sum[b]++ sum[s]-=2 这样网上更新的话 经过的点的sum值都变成1 祖先s的变成0 这样就实现了sum数组的维护 */ #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #define maxn 300100 using namespace std; int n,m,num,head[maxn],ans,inf; int fa[maxn][30],dep[maxn],dis[maxn],sum[maxn],edge[maxn]; struct node { int u,v,t,pre; }e[maxn*2]; struct Ans { int ai,bi,anc,di; }lca[maxn]; int init() { int x=0;char s=getchar(); while(s<'0'||s>'9')s=getchar(); while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x; } void Add(int from,int to,int dis) { num++; e[num].u=from; e[num].v=to; e[num].t=dis; e[num].pre=head[from]; head[from]=num; } void Dfs(int now,int from,int c,int Dis) { fa[now][0]=from; dep[now]=c;dis[now]=Dis; for(int i=head[now];i;i=e[i].pre) if(e[i].v!=from) { edge[e[i].v]=i; Dfs(e[i].v,now,c+1,Dis+e[i].t); } } void Get_fa() { for(int j=1;j<=16;j++) for(int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; } int Get_same(int a,int t) { for(int i=0;i<16;i++) if(t&(1<<i)) a=fa[a][i]; return a; } int LCA(int a,int b) { if(dep[a]<dep[b])swap(a,b); a=Get_same(a,dep[a]-dep[b]); if(a==b)return a; for(int i=16;i>=0;i--) if(fa[a][i]!=fa[b][i]) { a=fa[a][i]; b=fa[b][i]; } return fa[a][0]; } void Init() { n=init();m=init(); int u,v,t; for(int i=1;i<=n-1;i++) { u=init();v=init();t=init(); Add(u,v,t);Add(v,u,t); } Dfs(1,1,0,0); Get_fa(); for(int i=1;i<=m;i++) { lca[i].ai=init();lca[i].bi=init(); lca[i].anc=LCA(lca[i].ai,lca[i].bi); lca[i].di=dis[lca[i].ai]+dis[lca[i].bi]-2*dis[lca[i].anc]; inf=max(inf,lca[i].di); } } void Up_sum(int now,int from) { for(int i=head[now];i;i=e[i].pre) if(e[i].v!=from) { Up_sum(e[i].v,now); sum[now]+=sum[e[i].v]; } } int Judge(int x) { memset(sum,0,sizeof(sum)); int tot=0,dec=0; for(int i=1;i<=m;i++) if(lca[i].di>x)//非法路径 { tot++; dec=max(dec,lca[i].di-x);//最长非法路径与ans差值 sum[lca[i].ai]++; sum[lca[i].bi]++; sum[lca[i].anc]-=2; } Up_sum(1,1);//更新sum数组 for(int i=1;i<=n;i++) if(tot==sum[i]&&e[edge[i]].t>=dec)//删掉edge[i]这条边之后答案合法了 return 1; return 0; } void Solve()//二分答案 { int l=0,r=inf; while(l<=r) { int mid=(l+r)/2; int tmp=Judge(mid); if(tmp==1) { r=mid-1; ans=mid; } else l=mid+1; } } void Printf() { printf("%d\n",ans); } int main() { //freopen("transport.in","r",stdin); //freopen("transport.out","w",stdout); Init(); Solve(); Printf(); return 0; }