牛客练习赛51 F.ABCBA(树上主席树维护子序列)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客练习赛51 F.ABCBA(树上主席树维护子序列)相关的知识,希望对你有一定的参考价值。
题意
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(主席树+树上尺取)
p3302 [SDOI2013]森林(树上主席树+启发式合并)
2021牛客暑期多校训练营7 F.xay loves trees 主席树+dfs序