牛客练习赛51 F.ABCBA(树上主席树维护子序列)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客练习赛51 F.ABCBA(树上主席树维护子序列)相关的知识,希望对你有一定的参考价值。

LINK

题意

n n n个点的一棵树,每个点包含一个大写字母

询问 m m m次,每次问 v v v u u u路径组成的字符串中,串 A B C B A ABCBA ABCBA的数量


对于每组询问 l , r l,r l,r,若 l , r l,r l,r不在一条链上,可以先找出他们的 l c a lca lca x x x

可以拆分为 ( l , x ) (l,x) (l,x) ( x , r ) (x,r) (x,r)两条链计算答案,再合并起来

这样一来,我们需要维护一条链中 A B C B A ABCBA ABCBA所有子串的数量

发现这个东西是可以用线段树维护的

而且在一条链上的话,可以用主席树对每个节点维护一颗从根节点到本节点的线段树

保存每个深度的信息

但是查询 [ l , r ] [l,r] [l,r]的值时,不能像权值线段树那样直接减

还是需要分成 l o g log log个区间查询,再合并

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e4+10;
const int mod = 10007;
const int N = 5e6+10;
int n,q;
char s[maxn];
vector<int>vec[maxn];
struct rce 
{
    int ABCBA, A, AB, ABC, ABCB, B, BC, BCB, BCBA, C, CB, CBA, BA;
    rce operator+(const rce &t) const 
	{
        rce tmp;
        tmp.ABCBA = (ABCBA + t.ABCBA + A * t.BCBA + AB * t.CBA + ABC * t.BA + ABCB * t.A) % mod;
        tmp.A = (A + t.A) % mod;
        tmp.AB = (AB + t.AB + A * t.B) % mod;
        tmp.ABC = (ABC + t.ABC + A * t.BC + AB * t.C) % mod;
        tmp.ABCB = (ABCB + t.ABCB + A * t.BCB + AB * t.CB + ABC * t.B) % mod;
        tmp.B = (B + t.B) % mod;
        tmp.BC = (BC + t.BC + B * t.C) % mod;
        tmp.BCB = (BCB + t.BCB + B * t.CB + BC * t.B) % mod;
        tmp.BCBA = (BCBA + t.BCBA + B * t.CBA + BC * t.BA + BCB * t.A) % mod;
        tmp.C = (C + t.C) % mod;
        tmp.CB = (CB + t.CB + C * t.B) % mod;
        tmp.CBA = (CBA + t.CBA + C * t.BA + CB * t.A) % mod;
        tmp.BA = (BA + t.BA + B * t.A) % mod;
        return tmp;
    }
}su[N]; int id,ls[N],rs[N],root[maxn];
void update(int &rt,int pre,int l,int r,int pos,char c)
{
	rt = ++id;
	ls[rt] = ls[pre], rs[rt] = rs[pre];
	if( l==r )
	{
		if( c=='A' )	su[rt].A++;
		else if( c=='B' )	su[rt].B++;
		else if( c=='C' )	su[rt].C++;
		return;
	}
	int mid = l+r>>1;
	if( pos<=mid )	update( ls[rt],ls[pre],l,mid,pos,c );
	else	update( rs[rt],rs[pre],mid+1,r,pos,c );
	su[rt] = su[ls[rt]]+su[rs[rt]];
}
rce ask(int rt,int l,int r,int L,int R)
{
	if( l>=L && r<=R )	return su[rt];
	int mid = l+r>>1;
	if( R<=mid )	return ask( ls[rt],l,mid,L,R );
	else if( L>mid )	return ask( rs[rt],mid+1,r,L,R );
	else	return ask(ls[rt],l,mid,L,R)+ask(rs[rt],mid+1,r,L,R);
}
int fa[maxn][22],deep[maxn];
void predfs(int u,int father)
{
	deep[u] = deep[father]+1, fa[u][0] = father;
	for(int i=1;i<=20;i++)
		fa[u][i] = fa[fa[u][i-1]][i-1];
	for(auto v:vec[u] )
	{
		if( v==father )	continue;
		predfs( v,u );
	}
}
int lca(int x,int y)
{
	if( deep[x]<deep[y] )	swap( x,y );
	for(int i=20;i>=0;i--)
		if( deep[fa[x][i]]>=deep[y] )	x = fa[x][i];
	if( x==y )	return x;
	for(int i=20;i>=0;i--)
		if( fa[x][i]!=fa[y][i] )	x = fa[x][i],y = fa[y][i];
	return fa[x][0];
}
void dfs(int u,int father)
{
	update( root[u],root[father],1,n,deep[u],s[u] );
	for(auto v:vec[u] )
	{
		if( v==father )	continue;
		dfs( v,u );	
	}	
}
int main()
{
	cin >> n >> q >> ( s+1 );
	for(int i=1;i<n;i++)
	{
		int l,r; scanf("%d%d",&l,&r);
		vec[l].push_back( r ); vec[r].push_back( l );	
	}
	predfs(1,1);
	dfs(1,1);
	for(int i=1;i<=q;i++)
	{
		int l,r; scanf("%d%d",&l,&r);
		int x = lca(l,r), ans = 0;
		if( x==l )
			ans = ask( root[r],1,n,deep[l],deep[r] ).ABCBA;
		else if( x==r )
			ans = ask( root[l],1,n,deep[r],deep[l] ).ABCBA;
		else
		{
			rce w1 = ask( root[l],1,n,deep[x],deep[l] );
			rce w2 = ask( root[r],1,n,deep[x]+1,deep[r] );
			ans = ( ans+w1.ABCBA+w2.ABCBA )%mod;
			ans = ( ans+1ll*w1.A*w2.BCBA%mod )%mod;
			ans = ( ans+1ll*w1.BA*w2.CBA%mod )%mod;
			ans = ( ans+1ll*w1.CBA*w2.BA%mod )%mod;
			ans = ( ans+1ll*w1.BCBA*w2.A%mod )%mod;
		}
		printf("%d\\n",ans );
	}
	return 0;
} 

也可以区间dp,但是卡空间

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e4+10;
const int mod = 10007;
const int N = 5e6+10;
int n,q;
char s[maxn];
vector<int>vec[maxn];
struct rce
{
	int f[6][6];
	rce(){ memset( f,0,sizeof f ); }
}su[N]; int id,ls[N],rs[N],root[maxn];
rce merge(rce a,rce b)
{
	rce c;
	for(int l=1;l<=5;l++)
	for(int i=1;i+l-1<=5;i++)
	{
		int j = i+l-1;
		c.f[i][j] = a.f[i][j]+b.f[i][j];
		for(int k=i;k<j;k++)	c.f[i][j] += 1ll*a.f[i][k]*b.f[k+1][j]%mod;
		c.f[i][j] %= mod;
	}
	return c;
}
void update(int &rt,int pre,int l,int r,int pos,char c)
{
	rt = ++id;
	ls[rt] = ls[pre], rs[rt] = rs[pre];
	if( l==r )
	{
		if( c=='A' )	su[rt].f[1][1]++, su[rt].f[5][5]++;
		else if( c=='B' )	su[rt].f[2][2]++, su[rt].f[4][4]++;
		else if( c=='C' )	su[rt].f[3][3]++;
		return;
	}
	int mid = l+r>>1;
	if( pos<=mid )	update( ls[rt],ls[pre],l,mid,pos,c );
	else	update( rs[rt],rs[pre],mid+1,r,pos,c );
	su[rt] = merge( su[ls[rt]],su[rs[rt]] );
}
rce ask(int rt,int l,int r,int L,int R)
{
	if( l>=L && r<=R )	return su[rt];
	int mid = l+r>>1;
	if( R<=mid )	return ask( ls[rt],l,mid,L,R );
	else if( L>mid )	return ask( rs[rt],mid+1,r,L,R );
	else	return merge( ask(ls[rt],l,mid,L,R),ask(rs[rt],mid+1,

以上是关于牛客练习赛51 F.ABCBA(树上主席树维护子序列)的主要内容,如果未能解决你的问题,请参考以下文章

2021牛客暑期多校训练营7 F.xay loves trees(主席树+树上尺取)

Luogu 3302 森林(树上维护主席树)

p3302 [SDOI2013]森林(树上主席树+启发式合并)

2021牛客暑期多校训练营7 F.xay loves trees 主席树+dfs序

牛客多校7.F.xay loves trees 主席树+dfs序

牛课练习赛34 Flittle w and Discretization 主席树维护Mex