给出一棵树和m条路线,树上每条边都有长度.现在可以把一条边的长度置为0,求在置为0后,这m条路线的最大长度最小是多少.
链接
题解
首先有个显然的结论是置为0的边一定位于初始时的最长路线上.故可以二分答案,把所有长度大于二分值的路线上的所有边打上一次标记.然后检查最长路线上的所有边,当且仅当这条边被标记的次数恰好是长度大于二分值的路线的数量,以及把这条边置为0后可以保证最长路线的长度小于等于二分值,则该答案合法.标记路径可以使用树上差分解决.时间复杂度为
O(nlogn)
P.S.这道题最后一个点卡常QAQ偷偷开了O3才勉强卡过去.经过事后的测试,树剖求LCA比倍增求LCA大约可以节省10~20%的时间.所以正确的卡常姿势应该是拿树剖去写.
代码
#pragma GCC optimize(3)
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#define N 300010
#define LL long long
using namespace std;
int n,m,i,x,y,Z,lsum=1,L,Ans;
int head[N],d[N],fa[N][20],l[N][20],v[N],z[N];
struct Edge{
int t,next,l;
}e[N*2];
struct Data{
int x,y,l,lca;
}a[N];
inline void Swap(int &a,int &b){a^=b^=a^=b;}
const int MAXSIZE =1<<20;
char buf[MAXSIZE],*p1,*p2;
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXSIZE,stdin),p1==p2)?EOF:*p1++)
inline int rd(){
register int x=0;
char c=gc();
while (!isdigit(c)) c=gc();
while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=gc();
return x;
}
inline bool cmp(const Data &a,const Data &b){return (a.l<b.l);}
inline void Add(int s,int t,int l){
e[lsum].t=t; e[lsum].l=l; e[lsum].next=head[s]; head[s]=lsum++;
}
inline void Maketree(int x,int f){
register int i=0,j=0,p=0;
for (i=head[x];i;i=e[i].next){
if (e[i].t==f) continue;
p=e[i].t;
fa[p][0]=x; l[p][0]=e[i].l;
d[p]=d[x]+1;
for (j=1;j<=19;j++){
if (!fa[p][j-1]) break;
fa[p][j]=fa[fa[p][j-1]][j-1];
l[p][j]=l[fa[p][j-1]][j-1]+l[p][j-1];
}
Maketree(p,x);
}
}
inline int LCA(int x,int y){
register int sum=0,dis=0,i=0;
(d[x]<d[y])?Swap(x,y):void(0);
dis=d[x]-d[y];
for (i=0;i<=19&&dis>=(1<<i);++i)
(dis&(1<<i))?sum+=l[x][i],x=fa[x][i]:0;
if (x==y){
L=x; return sum;
}
for (i=19;i>=0;--i)
if (fa[x][i]!=fa[y][i]){
sum+=l[x][i]+l[y][i];
x=fa[x][i]; y=fa[y][i];
}
L=fa[x][0];
return sum+l[x][0]+l[y][0];
}
inline void Dfs(int x,int f){
for (register int i=head[x];i;i=e[i].next)
if (e[i].t!=f)
Dfs(e[i].t,x),v[x]+=v[e[i].t];
}
inline bool Check(int s){
register int i=0,sum=0,x=0,p=a[m].lca,q=a[m].l-s;
memset(v,0,sizeof(v));
for (i=m;i;--i){
if (a[i].l<=s) break;
++sum; ++v[a[i].x]; ++v[a[i].y];
v[a[i].lca]-=2;
}
Dfs(1,0);
for (i=1;i<=z[0];++i)
if (l[z[i]][0]>=q&&v[z[i]]>=sum) return 1;
return 0;
}
inline void Work(){
register int low=0,high=a[m].l,mid=0,x=0,p=a[m].lca;
x=a[m].x;
while (x!=p){
z[++z[0]]=x; x=fa[x][0];
}
x=a[m].y;
while (x!=p){
z[++z[0]]=x; x=fa[x][0];
}
while (low<=high){
mid=(low+high)>>1;
(Check(mid))?Ans=mid,high=mid-1:low=mid+1;
}
printf("%d\n",Ans);
}
int main(){
#ifdef __Marvolo
freopen("transport20.in","r",stdin);
freopen("zht.out","w",stdout);
#endif
n=rd(); m=rd();
for (i=1;i<n;i++){
x=rd(); y=rd(); Z=rd();
Add(x,y,Z); Add(y,x,Z);
}
Maketree(1,0);
for (i=1;i<=m;i++){
a[i].x=rd(); a[i].y=rd();
a[i].l=LCA(a[i].x,a[i].y);
a[i].lca=L;
}
sort(a+1,a+1+m,cmp);
Work();
return 0;
}
声明:
本文采用
BY-NC-SA
协议进行授权,如无注明均为原创,转载请注明转自
Marvolo
本文地址: [NOIP2015]运输计划-树上差分
本文地址: [NOIP2015]运输计划-树上差分