NOIP2015Day2T3运输计划(二分+树上差分)

  做了这么多NOIPTG的题,这是唯一 一道一眼秒的T3(有时候T2还不会做QAQ)。。。

  题目大意就不说了QWQ

  思路大概是:啊最大值最小化,来个二分。检验mid的话,显然就是用最长路径减去所有边权>mid的边的交边中最大值,如果不大于mid就可行,那树上差分呀!(完)

  神tmDay2 T1T3都有最大值最小化

  之前写过加点权的树上差分,就是这题(戳我)。。。然后自己yy出了边的树上差分,其实和点一个道理,因为每个点的父亲边只有一条,于是给这个点加一个权值数组表示父亲边被使用的次数。每有一条路径(u,v)加入就cnt[u]++,cnt[v]++,cnt[lca(u,v)]-=2,那某个点x的父亲边被使用的次数就是x这棵子树的权值和辣!

  一开始只拿了80分。。因为我在没有建好树的情况下,也就是刚刚读入两个点及其边权(x,y,z)时直接就val[y]++了,这时候还不知道x和y谁是父亲QAQ

代码如下:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
using namespace std;
struct poi{int too,pre,dis;}e[600010],lik[600010];
int n,m,x,y,z,l,r,mid,mx,val[300010],fa[300010],tot,tot2,q[300010][4],cntt[300010];
int cnt[300010],last[300010],last2[300010],d[300010];
bool v[300010];
void add(int x,int y,int z){e[++tot].too=y;e[tot].dis=z;e[tot].pre=last[x];last[x]=tot;}
void add2(int x,int y,int z){lik[++tot2].too=y;lik[tot2].dis=z;lik[tot2].pre=last2[x];last2[x]=tot2;}
int gf(int x){return fa[x]==x?x:fa[x]=gf(fa[x]);}
void tarjan(int x)
{
    v[x]=1;fa[x]=x;
    for(int i=last2[x];i;i=lik[i].pre)if(v[lik[i].too])q[lik[i].dis][3]=gf(lik[i].too);
    for(int i=last[x],too=e[i].too;i;i=e[i].pre,too=e[i].too)if(!v[too])d[too]=d[x]+e[i].dis,tarjan(too),fa[too]=x;
}
int dfs(int x,int fa)
{
    int sum=cnt[x];
    for(int i=last[x],too=e[i].too;i;i=e[i].pre,too=e[i].too)if(too!=fa)val[too]=e[i].dis,sum+=dfs(too,x);
    return cntt[x]=sum;
}
bool ck()
{
    int total=0,mxx=0;
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=m;i++)if(d[q[i][1]]+d[q[i][2]]-2*d[q[i][3]]>mid)total++,cnt[q[i][1]]++,cnt[q[i][2]]++,cnt[q[i][3]]-=2;
    dfs(1,0);
    for(int i=1;i<=n;i++)if(cntt[i]==total&&val[i]>mxx)mxx=val[i];
    return mx-mxx<=mid;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-1;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
    for(int i=1;i<=m;i++)scanf("%d%d",&x,&y),add2(x,y,i),add2(y,x,i),q[i][1]=x,q[i][2]=y;
    tarjan(1);
    for(int i=1;i<=m;i++)mx=max(mx,d[q[i][1]]+d[q[i][2]]-2*d[q[i][3]]);
    l=0;r=mx;
    while(l<r)
    {
        mid=(l+r)/2;
        if(ck())r=mid;else l=mid+1;
    }
    printf("%d
",l);
}
View Code
原文地址:https://www.cnblogs.com/Sakits/p/6505340.html