首页 > 其他 > 详细

[LOJ3066]\[ROI 2016]快递

时间:2020-03-03 22:46:01      阅读:62      评论:0      收藏:0      [点我收藏+]

[LOJ3066][ROI 2016]快递

(找不到题解,看了别人代码,自己理解了一下)

题意:给定一些路径,求最长的相交长度-1,并且输出方案

(注意没有相交时输出0而不是-1)

明显的结论:两条路径的 相交路径的LCA一定是某一条路径的LCA

暴力:我们可以通过求若干次LCA来得到一对路径的答案,枚举+RMQ求LCA就能做到\(O(n^2)\)

一、对于相交路径为单链的情况:

我们可以通过从每条路径的\(LCA\)向下二分找到最长的单链存在另一条路径包含它,这个可以用一些奇怪的数据结构(主席树)求出

#include<bits/stdc++.h>

using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

#define pb push_back
template <class T> inline void cmin(T &a,T b){ if(a>b) a=b; }
template <class T> inline void cmax(T &a,T b){ if(a<b) a=b; }

char IO;
template <class T=int> T rd(){
    T s=0;
    int f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}


const int N=2e5+10,K=30;

int n,m;
vector <int> G[N];
int a[N],b[N],lca[N],dep[N],fa[N][20];

int L[N],R[N],id[N],dfn;
void dfs(int u,int f) {
    id[L[u]=++dfn]=u;
    fa[u][0]=f;
    rep(i,1,18) fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int v:G[u]) if(v!=f) {
        dep[v]=dep[u]+1;
        dfs(v,u);
    }
    R[u]=dfn;
}

int Up(int x,int k) {
    for(reg int i=0;(1<<i)<=k;++i) if(k&(1<<i)) x=fa[x][i];
    return x;
}

int LCA(int x,int y) {
    if(dep[x]<dep[y]) swap(x,y);
    x=Up(x,dep[x]-dep[y]);
    if(x==y) return x;
    drep(i,18,0) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

vector <int> U[N];
int rt[N],cnt;
int ls[N*K],rs[N*K],s[N*K];
void Upd(int &p,int pre,int l,int r,int x) {
    p=++cnt;
    s[p]=s[pre]+1,ls[p]=ls[pre],rs[p]=rs[pre];
    if(l==r) return;
    int mid=(l+r)>>1;
    x<=mid?Upd(ls[p],ls[pre],l,mid,x):Upd(rs[p],rs[pre],mid+1,r,x);
}

int Que(int pl,int pr,int l,int r,int ql,int qr) {
    if(ql>qr) return 0;
    if(l==ql && r==qr) return s[pr]-s[pl];
    int mid=(l+r)>>1;
    if(qr<=mid) return Que(ls[pl],ls[pr],l,mid,ql,qr);
    else if(ql>mid) return Que(rs[pl],rs[pr],mid+1,r,ql,qr);
    else return Que(ls[pl],ls[pr],l,mid,ql,mid)+Que(rs[pl],rs[pr],mid+1,r,mid+1,qr);
}

int Get(int x,int y){
    if(dep[x]>dep[y]) swap(x,y);
    if(x!=y) {
        int t=Up(y,dep[y]-dep[x]-1);
        return Que(rt[0],rt[L[t]-1],1,n,L[y],R[y])+Que(rt[L[y]-1],rt[R[y]],1,n,R[t]+1,n);
    } else return Que(rt[0],rt[L[x]],1,n,L[y],R[y])+Que(rt[L[y]-1],rt[R[y]],1,n,R[x]+1,n);
}

int lst;

int Check(int mid) {
    rep(i,1,m) {
        if(dep[a[i]]-dep[lca[i]]>=mid) {
            int t=Up(a[i],dep[a[i]]-dep[lca[i]]-mid);
            if(Get(lca[i],t)>=2) {
                lst=i;
                return 1;
            }
        }
        if(dep[b[i]]-dep[lca[i]]>=mid) {
            int t=Up(b[i],dep[b[i]]-dep[lca[i]]-mid);
            if(Get(lca[i],t)>=2) {
                lst=i;
                return 1;
            }
        }
    }
    return 0;
}


namespace pt1{
    int Get(int a,int b,int c,int d) {
        return max(0,dep[LCA(b,d)]-max(dep[a],dep[c]));
    }
    int Ans(int i,int j){
        int x=LCA(a[i],b[i]),y=LCA(a[j],b[j]);
        int t=Get(x,a[i],y,a[j])+Get(x,a[i],y,b[j])+Get(x,b[i],y,a[j])+Get(x,b[i],y,b[j]);
        return t;
    }
}

int main(){
    n=rd(),m=rd();
    rep(i,2,n) {
        int f=rd();
        G[f].pb((int)i),G[i].pb(f);
    }
    dfs(1,0);
    rep(i,1,m) {
        a[i]=rd(),b[i]=rd();
        lca[i]=LCA(a[i],b[i]);
        int x=L[a[i]],y=L[b[i]];
        if(x>y) swap(x,y);
        U[x].pb(y);
    }
    rep(i,1,n) {
        rt[i]=rt[i-1];
        for(int j:U[i]) Upd(rt[i],rt[i],1,n,j);
    }
    int l=1,r=n,res=0;
    while(l<=r) {
        int mid=(l+r)>>1;
        if(Check(mid)) l=mid+1,res=mid;
        else r=mid-1;
    }
    printf("%d\n",res);
    if(res==0) {
        puts("1 2");
        return 0;
    }
    rep(i,1,m) if(i!=lst) {
        if(pt1::Ans(lst,i)>=res) {
            printf("%d %d\n",lst,i);
            return 0;
        }
    }
}

\[ \ \]

\[ \ \]

二、对于相交路径是两条垂下来的链的情况

技术分享图片

我们通过差分的方式在一个端点加入另一个端点

通过启发式合并转移到儿子

接下来我们考虑合并时的贡献

那么\(a_i\)\(a_j\)\(u\)处产生第一次合并,我们就知道答案是\(dep[u]+dep[LCA(b_i,b_j)]-dep[LCA(a_i,b_i)]\cdot 2\)

可以看到,只与\(dep[LCA(b_i,b_j)]\)有关

显然我们不能直接枚举,于是考虑插入\(b_i\)时,只与欧拉序最靠近的两个\(b_j\)产生计算,模拟\(RMQ\)\(LCA\)可以知道,这样的\(dep[LCA(b_i,b_j)]\)才有可能是最大的

由于需要找前驱后继,所以直接用\(set\)维护合并比较方便

#include<bits/stdc++.h>

using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

#define pb push_back
template <class T> inline void cmin(T &a,T b){ if(a>b) a=b; }
template <class T> inline void cmax(T &a,T b){ if(a<b) a=b; }

char IO;
template <class T=int> T rd(){
    T s=0;
    int f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}


const int N=2e5+10;

int n,m;
vector <int> G[N];
int a[N],b[N];

int line[N<<1],pos[N],lc,Log[N<<1],dep[N];
int s[20][N<<1];

void dfs(int u,int f) {
    line[pos[u]=++lc]=u;
    for(int v:G[u]) if(v!=f) {
        dep[v]=dep[u]+1,dfs(v,u);
        line[++lc]=u;
    }
}

void PreMake(){
    rep(i,2,lc) Log[i]=Log[i>>1]+1;
    rep(i,1,lc) s[0][i]=line[i];
    rep(i,1,Log[lc]) {
        int len=1<<(i-1);
        rep(j,1,lc+1-(1<<i)) {
            s[i][j]=dep[s[i-1][j]]<dep[s[i-1][j+len]]?s[i-1][j]:s[i-1][j+len];
        }
    }
}
int LCA(int x,int y) {
    x=pos[x],y=pos[y];
    if(x>y) swap(x,y);
    int d=Log[y-x+1];
    return dep[s[d][x]]<dep[s[d][y-(1<<d)+1]]?s[d][x]:s[d][y-(1<<d)+1];
}

struct Node{
    int x,y;
    bool operator < (const Node __) const {
        return x!=__.x?pos[x]<pos[__.x]:y<__.y;
    }
};

set <Node> S[N];
vector <Node> Add[N],Del[N];
int ans,ansx=1,ansy=2;

int Get(int a,int b,int c,int d) {
    return max(0,dep[LCA(b,d)]-max(dep[a],dep[c]));
}
void Upd_Ans(int i,int j) {
    if(i==j) return;
    int x=LCA(a[i],b[i]),y=LCA(a[j],b[j]);
    int t=Get(x,a[i],y,a[j])+Get(x,a[i],y,b[j])+Get(x,b[i],y,a[j])+Get(x,b[i],y,b[j]);
    if(t>ans) ans=t,ansx=i,ansy=j;
}

typedef set <Node> ::iterator iter;
void Insert(int u,Node x){
    iter it=S[u].lower_bound(x);
    if(it!=S[u].end()) Upd_Ans(x.y,it->y);
    if(it!=S[u].begin()) Upd_Ans(x.y,(--it)->y);
    S[u].insert(x);
}

void dfs_getans(int u,int f) {
    for(int v:G[u]) if(v!=f) {
        dfs_getans(v,u);
        if(S[v].size()>S[u].size()) swap(S[u],S[v]);
        for(Node i:S[v]) Insert(u,i);
    }
    for(Node i:Add[u]) Insert(u,i);
    for(Node i:Del[u]) S[u].erase(i);
}



int main(){
    n=rd(),m=rd();
    rep(i,2,n) {
        int f=rd();
        G[i].pb(f),G[f].pb(i);
    }
    dfs(1,0),PreMake();
    rep(i,1,m) {
        a[i]=rd(),b[i]=rd();
        int lca=LCA(a[i],b[i]);
        Add[a[i]].pb((Node){b[i],i}),Add[b[i]].pb((Node){a[i],i});
        Del[lca].pb((Node){b[i],i}),Del[lca].pb((Node){a[i],i});
    }
    dfs_getans(1,0);
    printf("%d\n%d %d\n",ans,ansx,ansy);
}

[LOJ3066]\[ROI 2016]快递

原文:https://www.cnblogs.com/chasedeath/p/12405160.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!