最近公共祖先+二分答案运输计划
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最近公共祖先+二分答案运输计划相关的知识,希望对你有一定的参考价值。
答案问的是最小值且取值具有单调性,所以可以二分。
首先可以确定虫洞一定在所有耗费时间超过mid的计划路径的交集上,把所有计划按花费时间来从大到小排序就可以很容易找出它们。
在check中用一个d[x]数组标记从x到根节点的路径被走了几次,d[u]++,d[v]++,d[lca]-=2,然后调用dfscheck回溯d数组求和即可求出交集。
最后在交集上尝试添加虫洞,如果新添加的虫洞能使原来耗费时间最大的计划能在mid时间以内完成,那么剩余所有的计划也都能按时完成,这时直接返回特殊值使所有dfscheck跳出。
注意n<=300000,所以求LCA时最大倍增次数为19
二分答案一定要用以下格式,要不然很容易陷入死循环或者得不到最优解
while(l<=r)
{
mid=(l+r)/2;
if (check())
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
1 #define stack_size (20001000) 2 int stack[stack_size],bak; 3 #include <cstdio> 4 #include <algorithm> 5 #include <cstring> 6 using namespace std; 7 int n,m,a,b,w,u,v,mid,fir[300001],dep[300001],f[300001][20],wsum[300001][20],d[300001],sz=0,r=0,chkcnt,root=1; 8 struct TE{int next,to,w;}te[600001]; 9 struct PLAN{int u,v,lca,w;}plan[300001]; 10 inline int read(int &x) 11 { 12 char ls=getchar();x=0; 13 for (;ls<‘0‘||ls>‘9‘;ls=getchar()); 14 for (;ls>=‘0‘&&ls<=‘9‘;ls=getchar())x=x*10+ls-‘0‘; 15 return x; 16 } 17 inline void adde(int &u,int &v,int &w) 18 { 19 te[++sz].next=fir[u]; 20 fir[u]=sz; 21 te[sz].to=v; 22 te[sz].w=w; 23 } 24 inline void dfs(int &x,int &p) 25 { 26 for (int i=1;i<=19;i++) 27 { 28 if ((1<<i)>dep[x]) break; 29 f[x][i]=f[f[x][i-1]][i-1]; 30 wsum[x][i]=wsum[x][i-1]+wsum[f[x][i-1]][i-1]; 31 } 32 for (int i=fir[x];i;i=te[i].next) 33 if (te[i].to!=p) 34 { 35 dep[te[i].to]=dep[x]+1; 36 f[te[i].to][0]=x; 37 wsum[te[i].to][0]=te[i].w; 38 dfs(te[i].to,x); 39 } 40 } 41 inline void lca() 42 { 43 for (int i=1;i<=m;i++) 44 { 45 int a=plan[i].u,b=plan[i].v,&ww=plan[i].w; 46 if (dep[a]>dep[b]){int t=a;a=b;b=t;} 47 int t=dep[b]-dep[a]; 48 for (int j=19;j>=0;j--) 49 if (t&(1<<j)) 50 { 51 ww+=wsum[b][j]; 52 b=f[b][j]; 53 } 54 for (int j=19;j>=0;j--) 55 if (f[a][j]!=f[b][j]) 56 { 57 ww+=wsum[a][j]+wsum[b][j]; 58 a=f[a][j],b=f[b][j]; 59 } 60 if (a==b) 61 plan[i].lca=a; 62 else 63 { 64 ww+=wsum[a][0]+wsum[b][0]; 65 plan[i].lca=f[a][0]; 66 } 67 if (ww>r) r=ww; 68 } 69 } 70 inline bool cmp(PLAN a,PLAN b) 71 {return a.w>b.w;} 72 inline int dfscheck(int &x,int &p) 73 { 74 int dsum=0,t; 75 for (int i=fir[x];i;i=te[i].next) 76 if (te[i].to!=p) 77 { 78 t=dfscheck(te[i].to,x); 79 if (t==-1) return -1; 80 if (t==chkcnt&&plan[1].w-te[i].w<=mid) return -1; 81 dsum+=t; 82 } 83 return dsum+d[x]; 84 } 85 inline bool check() 86 { 87 memset(d,0,sizeof(d)); 88 chkcnt=0; 89 for (int i=1;i<=m;i++) 90 { 91 if (plan[i].w<=mid) break; 92 chkcnt++; 93 d[plan[i].u]++; 94 d[plan[i].v]++; 95 d[plan[i].lca]-=2; 96 } 97 if (dfscheck(root,root)==-1) return 1; 98 return 0; 99 } 100 int main() 101 { 102 __asm__ __volatile__ 103 ( 104 "mov %%esp,%0\\n" 105 "mov %1,%%esp\\n" 106 :"=g"(bak) 107 :"g"(stack+stack_size-1) 108 : 109 ); 110 read(n);read(m); 111 for (int i=1;i<n;i++) 112 { 113 read(u);read(v);read(w); 114 adde(u,v,w); 115 adde(v,u,w); 116 } 117 dfs(root,root); 118 for (int i=1;i<=m;i++) 119 read(plan[i].u),read(plan[i].v); 120 lca(); 121 sort(plan+1,plan+m+1,cmp); 122 int l=0,ans; 123 while(l<=r) 124 { 125 mid=(l+r)/2; 126 if (check()) 127 { 128 ans=mid; 129 r=mid-1; 130 } 131 else 132 l=mid+1; 133 } 134 printf("%d",ans); 135 return 0; 136 }
以上是关于最近公共祖先+二分答案运输计划的主要内容,如果未能解决你的问题,请参考以下文章