zoukankan      html  css  js  c++  java
  • JZOJ5956.【NOIP2018模拟11.7A组】easy LCA

    PROBLEM

    给定一个n个节点的树,给定一个排列,求所有连续子段的节点的LCA的深度和。

    SOLUTION

    这题有很多种方法。

    分治

    考虑跨过区间中点的答案,从中线往两边扫,扫过左半边和右半边的LCA一定是在两条链上,那么合并这两条链上任意点对的答案,扫一遍就可以了。另外考虑O(1)求LCA,用欧拉序与RMQ可以做到总复杂度O(Nlog n),只不过常数巨大。

    一个性质

    对于一个排列,任意相邻位置求LCA,所有LCA中深度最小的就是整个排列的LCA。因此我们可以把这个问题转化为区间最值问题。先将相邻的LCA求出来,如果一个位置为选定区间的LCA,那么这个区间里就不能有深度比它小的,找到左边第一个小于它的,右边第一个小于等于它的,避免算重,用一个单调栈来维护一下就好了。

    3)线段树合并?

    单调栈

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 600010
    #define maxm 1200020
    #define maxp 20
    #define LL long long 
    using namespace std;
    
    int n,m,i,j,k,x,y,t,tot,lca,p[maxn],le[maxn],ri[maxn],b[maxn];
    int em,e[maxm],nx[maxm],ls[maxn],dep[maxn],d[maxn],fa[maxn][maxp+1];
    LL ans;
    
    void read(int &x){
    	x=0; char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar());
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; 
    }
    
    void insert(int x,int y){
    	em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
    	em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
    }
    
    void dfs(int x,int p){
    	fa[x][0]=p,dep[x]=dep[p]+1;
    	for(int i=1;i<=maxp;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
    		dfs(e[i],x);
    }
    
    int getlca(int x,int y){
    	if (dep[x]<dep[y]) swap(x,y);
    	for(int i=maxp;i>=0;i--) if (dep[fa[x][i]]>=dep[y])	x=fa[x][i];
    	if (x==y) return x;
    	for(int i=maxp;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    	return fa[x][0];
    }
    
    int main(){
    	freopen("easy.in","r",stdin);
    	freopen("easy.out","w",stdout);
    	read(n);
    	for(i=1;i<n;i++){
    		read(x),read(y);
    		insert(x,y);
    	}
    	dfs(1,0);
    	for(i=1;i<=n;i++) ans+=dep[i];
    	for(i=1;i<=n;i++) read(p[i]);
    	for(i=1;i<=n-1;i++) b[i]=getlca(p[i],p[i+1]);
    	d[t=1]=0;
    	for(i=1;i<=n-1;i++){
    		while (t&&dep[b[d[t]]]>dep[b[i]]) t--;
    		le[i]=d[t]+1;
    		d[++t]=i;
    	} 
    	d[t=1]=0;
    	for(i=n-1;i>=1;i--){
    		while (t&&dep[b[d[t]]]>=dep[b[i]]) t--;
    		if (d[t]==0) ri[i]=n-1;
    		else ri[i]=d[t]-1;
    		d[++t]=i;
    	}
    	for(i=1;i<=n-1;i++) ans+=(LL)(ri[i]-i+1)*(i-le[i]+1)*dep[b[i]];
    	printf("%lld",ans);
    }
    

    T飞了的RMQ

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 600010
    #define maxm 1200020
    #define maxp 20
    #define LL long long 
    using namespace std;
    
    int n,m,i,j,k,x,y,t,tot,lca,p[maxn],le[maxn],ri[maxn];
    int em,e[maxm],nx[maxm],ls[maxn],dep[maxn],d[maxn];
    int fir[maxn*2],a[maxn*2],f[maxn*2][maxp+1],g[maxn*2][maxp+1],b[maxn];
    LL ans;
    
    void read(int &x){
    	x=0; char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar());
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; 
    }
    
    void insert(int x,int y){
    	em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
    	em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
    }
    
    void dfs(int x,int p){
    	a[++a[0]]=x; fir[x]=a[0]; dep[x]=dep[p]+1;
    	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p) 
    		dfs(e[i],x),a[++a[0]]=x;
    }
    
    void RMQ(){
    	for(int i=1;i<=a[0];i++) f[i][0]=dep[a[i]],g[i][0]=a[i];
    	for(int j=1;j<=maxp&&(1<<j)<=a[0]-1;j++)
    		for(int i=1;i<=a[0]-(1<<j);i++){
    			int k=i+(1<<(j-1));
    			if (f[i][j-1]<f[k][j-1]) 
    				f[i][j]=f[i][j-1],g[i][j]=g[i][j-1];
    			else f[i][j]=f[k][j-1],g[i][j]=g[k][j-1];
    		}
    }
    
    int que(int x,int y){
    	if (x==y) return x;
    	if (fir[x]>fir[y]) swap(x,y);
    	k=log2(fir[y]-fir[x]);
    	if (f[fir[x]][k]<f[fir[y]-(1<<k)][k]) return g[fir[x]][k];
    	else return g[fir[y]-(1<<k)][k];
    }
    
    int main(){
    	freopen("easy.in","r",stdin);
    	freopen("easy.out","w",stdout);
    	read(n);
    	for(i=1;i<n;i++){
    		read(x),read(y);
    		insert(x,y);
    	}
    	dfs(1,0);
    	RMQ();
    	for(i=1;i<=n;i++) ans+=dep[i];
    	for(i=1;i<=n;i++) read(p[i]);
    	for(i=1;i<=n-1;i++) b[i]=que(p[i],p[i+1]);
    	d[t=1]=0;
    	for(i=1;i<=n-1;i++){
    		while (t&&dep[b[d[t]]]>dep[b[i]]) t--;
    		le[i]=d[t]+1;
    		d[++t]=i;
    	} 
    	d[t=1]=0;
    	for(i=n-1;i>=1;i--){
    		while (t&&dep[b[d[t]]]>=dep[b[i]]) t--;
    		if (d[t]==0) ri[i]=n-1;
    		else ri[i]=d[t]-1;
    		d[++t]=i;
    	}
    	for(i=1;i<=n-1;i++) ans+=(LL)(ri[i]-i+1)*(i-le[i]+1)*dep[b[i]];
    	printf("%lld",ans);
    }
    
  • 相关阅读:
    pip 安装依赖 requirements.txt
    TCP三次握手四次挥手详细理解附面试题
    装饰器修复技术
    BBS(第一篇)
    Auth模块
    Django--中间件
    cookie和session
    创建多对多表关系的三种方式
    批量插入数据,自定义分页器
    Django--几个重要方法优化(面试重点)
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/11700967.html
Copyright © 2011-2022 走看看