P2680 [NOIP2015 提高组] 运输计划

原题链接

考察:LCA+二分答案

这题也太难想了,本蒟蒻完全不会QAQ

思路:

       树上建边,然后每个任务都是一个查询,我们可以利用LCA求出树上任意两点的时间和.这道题要求去除一条边后最小的最大值.

       两个最字很容易想到二分答案.假设二分答案为mid.对于每一个任务,检测它的时间是否会>mid.如果会统计>的任务数cnt.我们要找到一条所有时间过大的任务的公共边.删除这条边后,能让所有任务<=mid.某条边在所有不合时间的任务里出现 == 某条边在所有不合时间的路径里出现 cnt次.快速求遍历边的次数可以想到树上差分.每次找到cnt次的最大边.在检查减去这条边后是否符合即可.

  1 #include <iostream> 
  2 #include <cstring>
  3 #include <queue>
  4 using namespace std;
  5 typedef long long LL;
  6 const int N = 300010,M = 20;
  7 int h[N],idx,n,m,fa[N][M],depth[N],d[N],dist[N],res,cnt; 
  8 struct Road{
  9     int fr,to,ne,w;
 10 }road[N<<1];
 11 struct Path{
 12     int u,v,anc;
 13     LL w;
 14 }path[N];
 15 void add(int a,int b,int w)
 16 {
 17     road[idx].w = w,road[idx].fr = a,road[idx].to = b,road[idx].ne = h[a],h[a] = idx++;
 18 }
 19 void bfs(int s)
 20 {
 21     queue<int> q;
 22     q.push(s);
 23     memset(depth,0x3f,sizeof depth);
 24     depth[s] = 1,depth[0] = 0;
 25     while(q.size())
 26     {
 27         int u = q.front();
 28         q.pop();
 29         for(int i=h[u];~i;i=road[i].ne)
 30         {
 31             int v = road[i].to;
 32             if(depth[v]>depth[u]+1)
 33             {
 34                 depth[v] = depth[u]+1;
 35                 dist[v] = dist[u]+road[i].w;
 36                 q.push(v);
 37                 fa[v][0] = u;
 38                 for(int j=1;j<=19;j++)
 39                   fa[v][j] = fa[fa[v][j-1]][j-1];
 40             }
 41         }
 42     }
 43 }
 44 int lca(int a,int b)
 45 {
 46     if(depth[a]<depth[b]) swap(a,b);
 47     for(int i=19;i>=0;i--)
 48      if(depth[fa[a][i]]>=depth[b]) a = fa[a][i];
 49     if(a==b) return a;
 50     for(int i=19;i>=0;i--)
 51      if(fa[a][i]!=fa[b][i]) a = fa[a][i],b = fa[b][i];
 52     return fa[a][0];
 53 }
 54 void dfs(int u,int fa)
 55 {
 56     for(int i=h[u];~i;i=road[i].ne)
 57     {
 58         int v =road[i].to;
 59         if(v==fa) continue;
 60         dfs(v,u);
 61         d[u]+=d[v];
 62     }
 63     int dis = dist[u];
 64     if(fa!=-1) dis-=dist[fa];
 65     if(cnt<=d[u]&&dis>res) res = dis;
 66 }
 67 bool check(LL mid)
 68 {
 69     cnt = 0;res = 0;
 70     memset(d,0,sizeof d);
 71     for(int i=1;i<=m;i++)
 72       if(path[i].w>mid)
 73       {
 74           cnt++;//求多少条不满足的路径 
 75           d[path[i].u]++,d[path[i].v]++,d[path[i].anc]-=2;
 76       }
 77     if(!cnt) return 1;
 78     dfs(1,-1);
 79     for(int i=1;i<=m;i++)
 80       if(path[i].w-res>mid) return 0;
 81     return 1;
 82 }
 83 int main()
 84 {
 85     scanf("%d%d",&n,&m);
 86     memset(h,-1,sizeof h);
 87     for(int i=1;i<n;i++)
 88     {
 89         int a,b,w; scanf("%d%d%d",&a,&b,&w);
 90         add(a,b,w); add(b,a,w);
 91     }
 92     bfs(1);
 93     LL l = 0,r = 0;
 94     for(int i=1;i<=m;i++)
 95     {
 96         int a,b; scanf("%d%d",&a,&b);
 97         int anc = lca(a,b);
 98         LL w = dist[a]+dist[b]-2*dist[anc];
 99         path[i] = {a,b,anc,w};
100         r=  max(w,r);
101     }
102     while(l<r)
103     {
104         LL mid = l+r>>1;
105         if(check(mid)) r = mid;
106         else l = mid+1;
107     }
108     printf("%lld\n",r);
109     return 0;
110 }

 

       

posted @ 2021-05-12 13:49  acmloser  阅读(71)  评论(0编辑  收藏  举报