刷题总结——运输计划(bzoj4326)
题目:
题目背景
NOIP2015 提高组 Day2 T3
题目描述
公元2044年,人类进入了宇宙纪元。
L国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了L国的所有星球。
小P掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j ,任意飞船驶过它所花费的时间为 tj ,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新,L国国王同意小P的物流公司参与L国的航道建设,即允许小P把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小P的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小P的物流公司的阶段性工作就完成了。
如果小P可以自由选择将哪一条航道改造成虫洞,试求出小P的物流公司完成阶段性工作所需要的最短时间是多少?
输入格式
第一行包括两个正整数 n、m,表示L国中星球的数量及小P公司预接的运输计划的数量,星球从 1 到 n 编号。
接下来 n-1 行描述航道的建设情况,其中第 i 行包含三个整数 ai, bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。
接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj ,表示第 j 个运输计划是从 uj 号星球飞往 vj 号星球。
输出格式
共 1 行,包含 1 个整数,表示小P的物流公司完成阶段性工作所需要的最短时间。
样例数据 1
备注
【样例1说明】
将第1条航道改造成虫洞:则三个计划耗时分别为:11、12、11,故需要花费的时间为12。
将第2条航道改造成虫洞:则三个计划耗时分别为:7、15、11,故需要花费的时间为15。
将第3条航道改造成虫洞:则三个计划耗时分别为:4、8、11,故需要花费的时间为11。
将第4条航道改造成虫洞:则三个计划耗时分别为:11、15、5,故需要花费的时间为15。
将第5条航道改造成虫洞:则三个计划耗时分别为:11、10、6,故需要花费的时间为11。
故将第3条或第5条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为11。
【数据范围】
所有测试数据的范围和特点如下表所示:
请注意常数因子带来的程序效率上的影响。
题解:
二分+树上差分(+dfs序)
首先二分答案··对大于答案的路径给路径上所有的边打上标记··
然后找出是否有条边为这些打标记的边的并集并且最长的一条打标记的路径减去这条边后总长度小于枚举的答案即可··
打标记的话可以用树链剖分··而我用的是差分··这里也第一次学到了打边上差分的方法··
对于一条路径x->y,我们只需tag[x]++,tag[y]++,tag[lca(x,y)]-=2即可····
然后统计tag的话可以直接dfs··也可以通过dfs序··我们从最大的dfs序向最小的枚举··然后依次将枚举的点的tag统计进入它的father即可···然而我是直接dfs的
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; const int N=3e5+5; int tot,first[N],next[N*2],go[N*2],deep[N],val[N*2],dis[N],tag[N],len[N]; int n,m,g[N][21],ans,Maxx,mem[N]; struct node { int a,b,val,lca; }edge[N]; bool cmp(node a,node b) { return a.val>b.val; } inline int R() { char c;int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } inline void comb(int a,int b,int c) { next[++tot]=first[a],first[a]=tot,go[tot]=b,val[tot]=c; next[++tot]=first[b],first[b]=tot,go[tot]=a,val[tot]=c; } inline void dfs1(int u,int fa) { for(int e=first[u];e;e=next[e]) { int v=go[e]; if(v==fa) continue; dis[v]=dis[u]+val[e];len[v]=val[e]; deep[v]=deep[u]+1,g[v][0]=u,dfs1(v,u); } } inline int get(int a,int b) { if(deep[a]<deep[b]) swap(a,b); int i,j; for(i=0;(1<<i)<=deep[a];i++);i--; for(j=i;j>=0;j--) if(deep[a]-(1<<j)>=deep[b]) a=g[a][j]; if(a==b) return a; for(i=20;i>=0;i--) if(g[b][i]!=g[a][i]) a=g[a][i],b=g[b][i]; return g[a][0]; } inline void dfs2(int u,int fa) { for(int e=first[u];e;e=next[e]) { int v=go[e]; if(v==fa) continue; dfs2(v,u);tag[u]+=tag[v]; } } inline bool jud(int u,int fa,int tim,int maxx) { if(tag[u]==tim&&len[u]>=maxx) return true; for(int e=first[u];e;e=next[e]) { int v=go[e]; if(v==fa) continue; if(jud(v,u,tim,maxx)) return true; } } inline bool check(int x) { int maxx=0,cnt=0; memset(tag,0,sizeof(tag)); for(int i=1;i<=m;i++) { if(edge[i].val>x) { maxx=max(maxx,edge[i].val-x);cnt++; tag[edge[i].a]++,tag[edge[i].b]++,tag[edge[i].lca]-=2; } else break; } if(!cnt) return true; dfs2(1,0); if(mem[cnt]) return mem[cnt]>=maxx; int Temp=0; for(int i=2;i<=n;i++) if(tag[i]==cnt) Temp=max(Temp,len[i]); mem[cnt]=Temp;return mem[cnt]>=maxx; return false; } inline void getans() { int le=0,ri=Maxx;ans=Maxx; while(le<=ri) { int mid=(le+ri)/2; if(check(mid)) ans=mid,ri=mid-1; else le=mid+1; } } int main() { //freopen("a.in","r",stdin); n=R(),m=R();int a,b,c; for(int i=1;i<n;i++) { a=R(),b=R(),c=R(); comb(a,b,c);Maxx+=c; } deep[1]=1;dfs1(1,0); for(int i=1;i<=20;i++) for(int j=1;j<=n;j++) g[j][i]=g[g[j][i-1]][i-1]; for(int i=1;i<=m;i++) { a=R(),b=R();int lca=get(a,b); edge[i].a=a,edge[i].b=b;edge[i].val=dis[a]+dis[b]-2*dis[lca]; edge[i].lca=lca; } sort(edge+1,edge+m+1,cmp); getans(); cout<<ans<<endl; return 0; }