首页 > 代码库 > 【最近公共祖先+二分答案】运输计划

【最近公共祖先+二分答案】运输计划

答案问的是最小值且取值具有单调性,所以可以二分。

首先可以确定虫洞一定在所有耗费时间超过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 }

 

【最近公共祖先+二分答案】运输计划