bzoj4326: NOIP2015 運輸計劃(lca+二分)

Hanks_o發表於2017-12-25

題目傳送門
好題啊。

解法:
比較綜合。。
我一開始想:
先把所有距離求出來。
然後二分答案。
所有的路徑距離>答案 說明需要建蟲洞。
在所有大於答案的路徑中是有交集的。
那麼我們只需要在交集中找到一條邊使得所有路徑-邊權<=答案。
然後用差分咯。
sum[i]表示1到i這條邊次數都加1。
然後就差分嘛。
對於每個x和y。
1到x路徑+1
1到y路徑+1
1到lca路徑-2就行了嘛。

程式碼實現:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct node {int x,y,c,next;}a[2100000];int len,last[1100000];
void ins(int x,int y,int c) {len++;a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
int n,m,mn[21][610000],dfn,dep[1100000],ys[1100000],bin[21],dist[1100000],Lca[1100000],to[1100000];//ys表示與父親的邊權 
void dfs(int x) {
    for(int k=last[x];k;k=a[k].next) {
        int y=a[k].y;
        if(y!=mn[0][x]) {mn[0][y]=x;dep[y]=dep[x]+1;ys[y]=a[k].c;to[y]=to[x]+a[k].c;dfs(y);}
    }
}
void work() {
    bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]*2;
    for(int j=1;j<=20;j++)for(int i=1;i<=n;i++)if(dep[i]>=bin[j])mn[j][i]=mn[j-1][mn[j-1][i]];
}
int lca(int x,int y) {
    if(dep[x]>dep[y])swap(x,y);
    for(int i=20;i>=0;i--)if(dep[y]-dep[x]>=bin[i])y=mn[i][y];
    if(x==y)return x;
    for(int i=20;i>=0;i--)if(mn[i][y]!=mn[i][x]&&dep[x]>=bin[i]){x=mn[i][x];y=mn[i][y];}
    return mn[0][x];
}
int xx[1100000],yy[1100000],sum[1100000];
void cf(int x) {
    for(int k=last[x];k;k=a[k].next) {
        int y=a[k].y;
        if(y!=mn[0][x]) {cf(y);sum[x]+=sum[y];}
    }
}
int main() {
    scanf("%d%d",&n,&m);len=0;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++) {int x,y,c;scanf("%d%d%d",&x,&y,&c);ins(x,y,c);ins(y,x,c);}
    mn[0][1]=0;dep[1]=0;ys[1]=0;to[1]=0;dfs(1);work();
    int l=1,r=0,mid,ans=0;
    for(int i=1;i<=m;i++) {
        scanf("%d%d",&xx[i],&yy[i]);Lca[i]=lca(xx[i],yy[i]);
        dist[i]=to[xx[i]]+to[yy[i]]-2*to[Lca[i]];r=max(r,dist[i]);
    }
    while(l<=r) {
        mid=(l+r)/2;int mmax=0,tot=0;memset(sum,0,sizeof(sum));
        for(int i=1;i<=m;i++)if(dist[i]>mid) {
            tot++;mmax=max(mmax,dist[i]-mid);sum[xx[i]]++;sum[yy[i]]++;sum[Lca[i]]-=2;
        }cf(1);
        bool bk=false;
        for(int i=1;i<=n;i++)if(sum[i]>=tot&&ys[i]>=mmax) {bk=true;break;}
        if(bk==true) {r=mid-1;ans=mid;}
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

相關文章