P11740 题解
dAniel_lele · · 题解
真不是给人写的。
先思考操作 3 怎么做。考虑求出每个字符串反串的 SA,然后分别找 lower_bound,相减即为答案。
再考虑操作 0,也就是说我们需要在反串的末尾加入字符的同时维护 SA。考虑使用平衡树维护之,每次需要比较两个前缀的大小,可以使用二分哈希比较,这里我们使用更为快捷好写的自然溢出,是可以通过的,可以发现本题出题人还是十分良心的,不像某题解审核组 i 姓同学一天到晚想着卡哈希。
然后是操作 1,本操作跟操作 3 本质相同,唯一的问题是
最后考虑操作 2,由于我们需要字符串赋值,我们显然不能重新构建一遍后缀平衡树。考虑将所有字符串放到一个 Trie 上,那么字符串对应的就是一个节点到根(操作 1 同样可以记录),这样赋值的问题就解决了。至于平衡树第一个想法便是对其进行可持久化,这样复杂度已经对了!然而这样大概会 MLE 且难写。考虑将 Trie 上所有节点对应的 SA 扔到一棵平衡树中,同时维护每个平衡树节点包含了多少个在每个字符串中的 Trie 上节点。对于赋值操作,可以记录一个
总复杂度
#include <bits/stdc++.h>
#define int long long
#define mid ((l+r)>>1)
using namespace std;
const unsigned int mul=200467;
unsigned int pw[1000005];
unsigned int shash[20][1000005];
int sval[1000005],slen;
int aimpos,aimadd;
int n;
namespace Trie{
unordered_map<signed,signed> f[500005];
signed up[20][500005],dep[500005],val[500005];
unsigned int hsh[20][500005];
int cnt;
void init(){
for(int i=1;i<=cnt;i++){
for(int j=0;j<20;j++) up[j][i]=0;
f[i].clear();
}
val[1]=-1;
cnt=1;
}
int add(int x,int y){
if(f[x][y]) return f[x][y];
f[x][y]=++cnt; dep[cnt]=dep[x]+1,val[cnt]=y;
up[0][cnt]=x; hsh[0][cnt]=y+1;
for(int j=1;j<=19;j++){
up[j][cnt]=up[j-1][up[j-1][cnt]];
hsh[j][cnt]=hsh[j-1][cnt]+hsh[j-1][up[j-1][cnt]]*pw[1<<(j-1)];
}
return cnt;
}
}
void gethash(vector<int> s){
slen=s.size(); sval[0]=-1;
for(int i=1;i<=slen;i++){
shash[0][i]=s[i-1]+1; sval[i]=s[i-1];
for(int j=1;(1<<j)<=i;j++) shash[j][i]=shash[j-1][i]+shash[j-1][i-(1<<(j-1))]*pw[1<<(j-1)];
}
}
int cmp(int x,int y){
if(x<0){
if(x==-1){
x=slen;
for(int i=19;i>=0;i--){
if((1<<i)<=min((int)Trie::dep[y],x)){
if(Trie::hsh[i][y]==shash[i][x]){
y=Trie::up[i][y];
x-=(1<<i);
}
}
}
return (sval[x]<Trie::val[y])?0:((Trie::val[y]==sval[x])?1:2);
}
else{
// cout<<aimpos<<" "<<aimadd<<" "<<y<<"\n";
x=aimpos;
for(int i=19;i>=0;i--){
if((1<<i)<=min(Trie::dep[x],Trie::dep[y])){
if(Trie::hsh[i][x]==Trie::hsh[i][y]){
x=Trie::up[i][x];
y=Trie::up[i][y];
}
}
}
// cout<<x<<" "<<y<<"\n";
if(x!=1) return (Trie::val[x]<Trie::val[y])?0:((Trie::val[x]==Trie::val[y])?1:2);
if(Trie::val[y]==aimadd){
y=Trie::up[0][y];
if(Trie::val[y]==-1) return 1;
return 0;
}
return (aimadd<Trie::val[y])?0:2;
}
}
else{
// bool rep=0;
// if(x==7&&y==2) cout<<Trie::hsh[0][7]<<" "<<Trie::hsh[0][2]<<" OK\n",rep=1;
for(int i=19;i>=0;i--){
if((1<<i)<=min(Trie::dep[x],Trie::dep[y])){
if(Trie::hsh[i][x]==Trie::hsh[i][y]){
x=Trie::up[i][x];
y=Trie::up[i][y];
}
}
}
// if(rep) cout<<x<<" "<<y<<"\n";
return (Trie::val[x]<Trie::val[y])?0:((Trie::val[x]==Trie::val[y])?1:2);
}
}
int res[5],root;
namespace Tree{
const double alpha=0.75;
const int N=2000005;
int ver[N],tsz[N][5],sz[N][5],L[N],R[N],tag[N][5];
int buffer_size,buffer[N],tmp_size[N][5];
int ntsz[5],nsz[5],ntag[5];
void init(){
root=1;
}
void new_node(int &rt,int p){
rt=p; ver[rt]=1;
for(int i=0;i<n;i++) tsz[rt][i]=sz[rt][i]=0,tag[rt][i]=i;
L[rt]=R[rt]=0;
}
void pushup(int rt){
if(!rt) return ;
for(int i=0;i<n;i++) tsz[rt][i]=tsz[L[rt]][i]+sz[rt][i]+tsz[R[rt]][i];
ver[rt]=ver[L[rt]]+ver[R[rt]]+1;
}
void addtrans(int rt,int to[5]){
for(int i=0;i<n;i++){
ntsz[i]=tsz[rt][to[i]];
nsz[i]=sz[rt][to[i]];
ntag[i]=tag[rt][to[i]];
}
for(int i=0;i<n;i++){
tsz[rt][i]=ntsz[i];
sz[rt][i]=nsz[i];
tag[rt][i]=ntag[i];
}
}
void pushdown(int rt){
if(L[rt]) addtrans(L[rt],tag[rt]);
if(R[rt]) addtrans(R[rt],tag[rt]);
for(int i=0;i<n;i++) tag[rt][i]=i;
}
bool balance(int rt){
return alpha*ver[rt]>max(ver[L[rt]],ver[R[rt]]);
}
void flatten(int rt){
if(!rt) return ;
pushdown(rt);
flatten(L[rt]);
buffer[++buffer_size]=rt;
for(int i=0;i<n;i++) tmp_size[buffer_size][i]=sz[rt][i];
flatten(R[rt]);
}
void build(int &rt,int l,int r){
if(l>r){
rt=0; return ;
}
rt=buffer[mid];
for(int i=0;i<n;i++) sz[rt][i]=tmp_size[mid][i];
build(L[rt],l,mid-1),build(R[rt],mid+1,r);
pushup(rt);
}
void rebuild(int &rt){
buffer_size=0;
flatten(rt);
build(rt,1,buffer_size);
}
void add(int &rt,int p,int cg){
// cout<<rt<<" "<<p<<" "<<cg<<"\n";
if(!rt){
new_node(rt,p);
tsz[rt][cg]=sz[rt][cg]=1;
return ;
}
int sta=cmp(p,rt);
if(sta==1){
tsz[rt][cg]++,sz[rt][cg]++;
return ;
}
pushdown(rt);
if(sta==0) add(L[rt],p,cg);
if(sta==2) add(R[rt],p,cg);
pushup(rt);
if(!balance(rt)) rebuild(rt);
}
void qry1(int &rt,int p){ // <p
if(!rt) return ;
int sta=cmp(p,rt);
pushdown(rt);
if(sta==0){
qry1(L[rt],p);
return ;
}
if(sta==1){
for(int i=0;i<n;i++) res[i]+=tsz[L[rt]][i];
return ;
}
if(sta==2){
for(int i=0;i<n;i++) res[i]+=tsz[L[rt]][i]+sz[rt][i];
qry1(R[rt],p);
}
}
void qry2(int &rt,int p){ // <=p
if(!rt) return ;
int sta=cmp(p,rt);
pushdown(rt);
if(sta==0){
qry2(L[rt],p);
return ;
}
for(int i=0;i<n;i++) res[i]+=tsz[L[rt]][i]+sz[rt][i];
if(sta==2) qry2(R[rt],p);
}
void debug(int &rt){
if(!rt) return ;
cout<<rt<<" "<<L[rt]<<" "<<R[rt]<<"\n";
for(int i=0;i<n;i++) cout<<sz[rt][i]<<" "<<tsz[rt][i]<<" "<<tag[rt][i]<<" "; cout<<"\n";
debug(L[rt]),debug(R[rt]);
}
}
int nowed[5];
int remed[200005][5];
signed main(){
pw[0]=1; for(int i=1;i<=1000000;i++) pw[i]=pw[i-1]*mul;
Trie::init(),Tree::init();
int m,ty; cin>>n>>m>>ty;
int lsta=0;
for(int i=0;i<n;i++){
int len; cin>>len;
nowed[i]=1;
for(int j=1;j<=len;j++){
int v; cin>>v;
nowed[i]=Trie::add(nowed[i],v);
// cout<<nowed[i]<<" ";
Tree::add(root,nowed[i],i);
}
// Tree::debug(root);
// cout<<"\n";
}
for(int i=0;i<n;i++) remed[0][i]=nowed[i];
int q; cin>>q;
for(int i=1;i<=q;i++){
// Tree::debug(root);
int tr=lsta*ty;
int opt; cin>>opt; opt^=tr;
if(opt==0){
int x,c; cin>>x>>c; x^=tr,c^=tr; x--;
nowed[x]=Trie::add(nowed[x],c);
// cout<<x<<" "<<nowed[x]<<"\n";
Tree::add(root,nowed[x],x);
}
else if(opt==1){
int x,y,k,l,r; cin>>x>>y>>k>>l>>r; x^=tr,y^=tr,k^=tr,l^=tr,r^=tr; x--,y--;
// cout<<remed[y][k]<<"\n";
int ans[5]; memset(ans,0,sizeof(ans));
memset(res,0,sizeof(res));
aimpos=remed[k][y]; aimadd=l;
Tree::qry1(root,-2);
for(int i=0;i<n;i++) ans[i]-=res[i];//cout<<res[i]<<" "; cout<<"\n";
memset(res,0,sizeof(res));
aimpos=remed[k][y]; aimadd=r+1;
Tree::qry1(root,-2);
for(int i=0;i<n;i++) ans[i]+=res[i];//cout<<res[i]<<" "; cout<<"\n";
cout<<(lsta=ans[x])<<"\n";
}
else if(opt==2){
int x,y; cin>>x>>y; x^=tr,y^=tr; x--,y--;
nowed[x]=nowed[y];
int to[5]; for(int j=0;j<n;j++) to[j]=j; to[x]=y;
Tree::addtrans(root,to);
}
else{
int l,r; cin>>l>>r>>slen; l^=tr,r^=tr,slen^=tr;
vector<int> vc;
for(int i=1;i<=slen;i++){
int v; cin>>v; v^=tr; vc.push_back(v);
}
vector<int> ex;
int ans[5]; memset(ans,0,sizeof(ans));
ex.clear(); ex.push_back(l); for(auto v:vc) ex.push_back(v); gethash(ex);
memset(res,0,sizeof(res));
Tree::qry1(root,-1);
for(int i=0;i<n;i++) ans[i]-=res[i];//cout<<res[i]<<" "; cout<<"\n";
ex.clear(); ex.push_back(r+1); for(auto v:vc) ex.push_back(v); gethash(ex);
memset(res,0,sizeof(res));
Tree::qry1(root,-1);
for(int i=0;i<n;i++) ans[i]+=res[i];//cout<<res[i]<<" "; cout<<"\n";
for(int i=0;i<n;i++) cout<<(lsta=ans[i])<<" "; cout<<"\n";
}
for(int j=0;j<n;j++) remed[i][j]=nowed[j];
}
return 0;
}