NOIP2015 D2T3 运输计划

题目链接:满大街都是,不放了

题目大意:

给出一棵\(n\)个结点的树\((n\leq300000)\),每条边有正边权,现给出\(m\)组运输方案\([u,v]\),代价为\(u\)\(v\)上路径边权之和。现在你可以将一条边的边权变为0,使得所有运输方案中代价最大值最小,求这个最小值。

解析:

首先不难想到二分答案,这样问题变成了判断能否使原树的一条边边权为0,使所有方案的代价均小于\(K\)

对于每组计划我们可以用LCA在\(O(nlogn)\)的时间内求出所有计划所需的原代价,不难想到如果存在将一条边边权变为0,使所有计划合法的方案,那么它一定在所有代价超过\(K\)的计划路径的交集中,这样我们可以用树上差分找出被覆盖最多的边,假设将它的边权变为0,判断计划最大代价减去这条边边权后能否小于\(K\)

时间复杂度为\(O(nlogn+(m+n)logK)\)

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cctype>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())

using namespace std;

const int maxn=300000+26,maxlogn=18+3;

struct edge{
  int v; int val;
  edge(int v,int val):v(v),val(val) {}
};

int V[maxn],S[maxn],lca[maxn],u[maxn],v[maxn],depth[maxn],F[maxn][maxlogn+2],n,m,x,y;

int D[maxn][maxlogn+2],dis[maxn],k,L,R,mid;

vector<edge> g[maxn],G[maxn];

int readint()
{
 char c=getchar();
 while (!isdigit(c) && (c!='-')) c=getchar();
 
 int mark;

 if (c=='-') {mark=-1;c=getchar();} else mark=1;

 int x=0;

 while (isdigit(c))
 {
  x=x*10+(c-'0');
  c=getchar();
 }

 return x*mark;
}

void trans(int k,int fa,int d)
{
 F[k][0]=fa,depth[k]=d;

 rep(i,0,sz(g[k])-1)
  if (g[k][i].v!=fa) G[k].push_back(g[k][i]),trans(g[k][i].v,k,d+1); else D[k][0]=g[k][i].val;
}

void LCA_init()
{
 rep(i,1,maxlogn)
  rep(j,1,n)
   if (F[j][i-1]<0)
    F[j][i]=-1,D[j][i]=-1; 
   else
    D[j][i]=D[F[j][i-1]][i-1]+D[j][i-1],F[j][i]=F[F[j][i-1]][i-1];
}

void query(int &l,int &dis,int u,int v)
{
 dis=0;
 if (depth[u]<depth[v]) swap(u,v);

 rep(i,0,maxlogn-1)
  if ((depth[u]-depth[v])&(1<<i)) dis+=D[u][i],u=F[u][i];

 if (u==v) {l=u;return;}

 dep(i,maxlogn-1,0) if (F[u][i]!=F[v][i]) dis+=(D[u][i]+D[v][i]),u=F[u][i],v=F[v][i];
 dis+=D[u][0]+D[v][0],l=F[u][0];
}

void count(int k)
{
 S[k]=V[k];
 rep(i,0,sz(G[k])-1) count(G[k][i].v),S[k]+=S[G[k][i].v];
}

bool check(int k)
{
 memset(V,0,sizeof(V));
 int tot=0; int maxv=0,maxt=0;

 rep(i,1,m) if (dis[i]>k) maxv=max(maxv,dis[i]),tot++,V[u[i]]++,V[v[i]]++,V[lca[i]]-=2;
 
 count(1);

 rep(i,2,n)
  if (S[i]==tot) maxt=max(maxt,D[i][0]);
 
 if (!maxt) return false;
 if ((maxv-maxt)<=k) return true; else return false;
}

int main()
{
 scanf("%d%d",&n,&m);
 rep(i,1,n-1)
 {
  x=readint(),y=readint();k=readint();

  g[x].push_back(edge(y,k));
  g[y].push_back(edge(x,k));
 }

 trans(1,-1,1),LCA_init();

 L=0,R=0;
 
 rep(i,1,m)
 {
  scanf("%d%d",&u[i],&v[i]);
  query(lca[i],dis[i],u[i],v[i]);
  
  R=max(R,dis[i]);
 }


 while (L<(R-1))
 {
  mid=(L+R)>>1;
  if (check(mid)) R=mid; else L=mid+1;
 }
 
 if (check(L)) printf("%d\n",L); else printf("%d\n",R);
 
 return 0;
}

常数被卡了,消失的5分……

posted @ 2016-10-09 19:54  Krew  阅读(493)  评论(0)    收藏  举报