zoukankan      html  css  js  c++  java
  • [LOJ3066]\[ROI 2016]快递

    [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;
    		}
    	}
    }
    

    \[\ \]

    \[\ \]

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

    无标题.png

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

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

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

    那么\(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);
    }
    
  • 相关阅读:
    RHEL7 timedatectl命令
    广告点击率的贝叶斯平滑
    Expectation Propagation: Theory and Application
    微博推荐算法学习(Weibo Recommend Algolrithm)
    百度技术沙龙第48期回顾:大规模机器学习(含资料下载)
    内容匹配广告投放技术4:网盟CTR预估(百度文库课程)
    Logistic Regression的几个变种
    广告点击率预测 [离线部分]
    GBDT(MART) 迭代决策树入门教程 | 简介
    xgboost: 速度快效果好的boosting模型
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12405160.html
Copyright © 2011-2022 走看看