F. Paper Grading
大佬题解
一般关于前缀的问题基本都是Trie树。
首先将所给字符串建立一棵Trie树,Trie能够解决一个字符串在一个字符串集合中出现的次数,而查询前缀次数只需要找到Trie树中所给字符末尾的位置,那么其子树中打标记的次数即前缀次数。
由于子树dfs序[L,R]连续,于是把字典树按照dfs序标记,即变成区间查询问题[l,r]中[L,R]之间数的个数,二位偏序问题。
树套树(树状数组套下标线段树)即可解决,要动态开点!
#include<iostream>
using namespace std;
const int N=200010;
char s[N];
int n,q,pos[N];
// Trie树
struct T1
{
int tree[N][26],idx;
int insert(char s[])
{
int p=0;
for(int i=0;s[i];i++)
{
int t=s[i]-'a';
if(!tree[p][t]) tree[p][t]=++idx;
p=tree[p][t];
}
return p;
}
int find(char s[],int k)
{
int p=0;
for(int i=0;i<k;i++)
{
int t=s[i]-'a';
if(!tree[p][t]) return -1;
p=tree[p][t];
}
return p;
}
}Trie;
// 动态开点线段树
struct T2
{
struct node
{
int l,r;
int sz;
}tree[N*40];
int root[N],cnt;
void update(int &u,int l,int r,int pos,int x)
{
if(!u) u=++cnt;
tree[u].sz+=x;
if(l==r) return;
int mid=l+r>>1;
if(pos<=mid) update(tree[u].l,l,mid,pos,x);
else update(tree[u].r,mid+1,r,pos,x);
}
int query(int u,int l,int r,int L,int R)
{
if(!u) return 0;
if(L<=l&&r<=R) return tree[u].sz;
int mid=l+r>>1;
int v=0;
if(L<=mid) v+=query(tree[u].l,l,mid,L,R);
if(R>mid) v+=query(tree[u].r,mid+1,r,L,R);
return v;
}
}Segment;
// dfs序转化为区间
int dfn[N],sz[N],timestamp;
void dfs(int u)
{
dfn[u]=++timestamp;
sz[u]=1;
for(int i=0;i<26;i++)
if(Trie.tree[u][i])
dfs(Trie.tree[u][i]),sz[u]+=sz[Trie.tree[u][i]];
}
// 树状数组
int lowbit(int x) {return x&-x;}
void add(int k,int pos,int x)
{
for(;k<=n;k+=lowbit(k))
Segment.update(Segment.root[k],1,timestamp,pos,x);
}
int sum(int k,int L,int R)
{
int res=0;
for(;k;k-=lowbit(k))
res+=Segment.query(Segment.root[k],1,timestamp,L,R);
return res;
}
int main()
{
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>s;
pos[i]=Trie.insert(s);
}
dfs(0);
for(int i=1;i<=n;i++)
add(i,dfn[pos[i]],1);
while(q--)
{
int op;
cin>>op;
if(op==1)
{
int u,v;
cin>>u>>v;
add(u,dfn[pos[u]],-1);
add(v,dfn[pos[v]],-1);
add(u,dfn[pos[v]],1);
add(v,dfn[pos[u]],1);
swap(pos[u],pos[v]);
}
else
{
int k,l,r;
cin>>s;
cin>>k>>l>>r;
int u=Trie.find(s,k);
if(u==-1) cout<<0<<'\n';
else
{
int L=dfn[u],R=dfn[u]+sz[u]-1;
cout<<sum(r,L,R)-sum(l-1,L,R)<<'\n';
}
}
}
return 0;
}
cdq分治,带修改二维数点,把时间轴当作一维即静态三维数点,cdq分治+树状数组
#include<iostream>
#include<algorithm>
using namespace std;
const int N=200010;
char s[N];
int n,m,pos[N];
// Trie树
struct T1
{
int tree[N][26],idx;
int insert(char s[])
{
int p=0;
for(int i=0;s[i];i++)
{
int t=s[i]-'a';
if(!tree[p][t]) tree[p][t]=++idx;
p=tree[p][t];
}
return p;
}
int find(char s[],int k)
{
int p=0;
for(int i=0;i<k;i++)
{
int t=s[i]-'a';
if(!tree[p][t]) return -1;
p=tree[p][t];
}
return p;
}
}Trie;
// dfs序转化为区间
int dfn[N],sz[N],timestamp;
void dfs(int u)
{
dfn[u]=++timestamp;
sz[u]=1;
for(int i=0;i<26;i++)
if(Trie.tree[u][i])
dfs(Trie.tree[u][i]),sz[u]+=sz[Trie.tree[u][i]];
}
int ans[N];
struct node
{
int op;
int a,b,c,cnt;
int sign,id;
}q[N*5];
int st[N];
bool cmpb(const node &x,const node &y)
{
return x.b<y.b||x.b==y.b&&x.op<y.op;
}
int fw[N];
int lowbit(int x){return x&-x;}
void update(int k,int x){for(;k<=timestamp;k+=lowbit(k)) fw[k]+=x;}
int query(int k){int res=0;for(;k;k-=lowbit(k)) res+=fw[k];return res;}
void solve(int l,int r)
{
if(l>=r) return;
int mid=l+r>>1;
solve(l,mid),solve(mid+1,r);
int i=l;
for(int j=mid+1;j<=r;j++)
{
if(q[j].op==1) continue;
while(i<=mid&&q[i].b<=q[j].b)
{
if(q[i].op==1) update(q[i].c,q[i].cnt);
i++;
}
ans[q[j].id]+=q[j].sign*query(q[j].c);
}
while(i>l)
{
i--;
if(q[i].op==1)
update(q[i].c,-q[i].cnt);
}
inplace_merge(q+l,q+mid+1,q+r+1,cmpb);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s;
pos[i]=Trie.insert(s);
}
dfs(0);
int cnt=0;
for(int i=1;i<=n;i++)
q[++cnt]={1,0,i,dfn[pos[i]],1};
for(int i=1;i<=m;i++)
{
int op;
cin>>op;
if(op==1)
{
int u,v;
cin>>u>>v;
q[++cnt]={1,i,u,dfn[pos[u]],-1};
q[++cnt]={1,i,v,dfn[pos[v]],-1};
q[++cnt]={1,i,u,dfn[pos[v]],+1};
q[++cnt]={1,i,v,dfn[pos[u]],+1};
swap(pos[u],pos[v]);
}
else
{
st[i]=1;
int k,l,r;
cin>>s;
cin>>k>>l>>r;
int u=Trie.find(s,k);
if(u==-1) ans[i]=0;
else
{
int L=dfn[u],R=dfn[u]+sz[u]-1;
q[++cnt]={2,i,r,R,0,1,i};
q[++cnt]={2,i,l-1,R,0,-1,i};
q[++cnt]={2,i,r,L-1,0,-1,i};
q[++cnt]={2,i,l-1,L-1,0,1,i};
}
}
}
solve(1,cnt);
for(int i=1;i<=m;i++)
if(st[i]) cout<<ans[i]<<'\n';
return 0;
}
写代码过程中总是弄不清记得东西,每次都是一层一层的想,尤其是dfs序问题,很迷糊,还是要多写,多积累!!!要不然训练总是挂机