NOIP 2015 运输计划

来源:互联网 发布:序列比对动态规划算法 编辑:程序博客网 时间:2024/05/21 10:34

评测传送

二分答案+LCA+树上差分

最好用Tarjan求LCA,有的oj会卡倍增。

做法:
我们用LCA求出需要查询的每个计划的路径长度。
然后二分答案,check( )的时候,我们把大于mid的路径(因为这一些都是要去边的)求一下交点(边),
如果并非全都都交于一条边或者去掉交边后也不能让这些路径都小于等于mid,那么mid就是不可以的。
求交边时,用到树上差分。

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#define LL long longusing namespace std;const int N=300009;int head[N],nxt[2*N],to[2*N],w[2*N],tot;int qh[N],qn[2*N],qt[2*N],qtt,lca[2*N];bool vis[N];int f[N],dep[N],a[N],b[N],dis[N],s[N],v[N];int n,m;void add(int x,int y,int z){    to[++tot]=y;    nxt[tot]=head[x];    head[x]=tot;    w[tot]=z;} void add2(int x,int y){    qt[++tot]=y;    qn[tot]=qh[x];    qh[x]=tot;}int find(int x){    return f[x]==x?x:f[x]=find(f[x]); } void dfs(int x)//Tarjan找LCA {    vis[x]=1;    f[x]=x;    for(int i=head[x];i;i=nxt[i])    {        if(!vis[to[i]])        {            dep[to[i]]=dep[x]+w[i];            dfs(to[i]);            f[to[i]]=x;            v[to[i]]=w[i];        }     }     for(int i=qh[x];i;i=qn[i])    {        if(vis[qt[i]])        {            lca[i]=find(qt[i]);            if(i%2) lca[i+1]=lca[i];            else lca[i-1]=lca[i];        }    }}void sum(int father,int x)//树上差分{    for(int i=head[x];i;i=nxt[i])    {        if(to[i]!=father)        {            sum(x,to[i]);            s[x]+=s[to[i]];//s[x]表示的是x这个点与它的儿子的边在路径中出现的次数        }    }}bool check(int x){    int cnt=0,d=0;//d表示需要去掉的最大长度     memset(s,0,sizeof(s));    for(int i=1;i<=m;i++)    {        if(dis[i]>x){            cnt++;            d=max(d,dis[i]-x);            s[a[i]]++;            s[b[i]]++;            s[lca[i*2]]-=2;//树上差分        }    }     sum(1,1);    for(int i=1;i<=n;i++)     if(s[i]==cnt&&v[i]>=d) return 1;    return 0;}int main(){    freopen("a.in","r",stdin);    scanf("%d%d",&n,&m);    for(int i=1;i<n;i++)    {        int x,y,z;        scanf("%d%d%d",&x,&y,&z);        add(x,y,z);add(y,x,z);    }    tot=0;    for(int i=1;i<=m;i++)    {        scanf("%d%d",&a[i],&b[i]);        add2(a[i],b[i]);add2(b[i],a[i]);    }    dfs(1);    for(int i=1;i<=m;i++)     dis[i]=dep[a[i]]+dep[b[i]]-2*dep[lca[i*2]];//第i个计划两个城市的距离     int L=0,R=N*1000,mid;    while(L<=R)    {        mid=(L+R)>>1;        if(check(mid)) R=mid-1;        else L=mid+1;    }    printf("%d\n",L);    return 0;}