The 2019 ICPC Asia Nanjing Regional Contest F Paper Grading
思路
看到网上都写树套树?就我直观的想法是离线然后\(cdq\)吗...
发现比较麻烦的是那个交换操作,考虑对询问离线,那么每个原串对答案的贡献就被交换操作分为\(O(m)\)个在一个时间段上的贡献。把原串的时间段和询问的下标区间都分为“后减前”这两段,转化为二维偏序问题。
考虑如何处理lcp的条件限制,原本以为可以直接对原串暴力在trie树上跑一遍产生贡献,询问直接在串对应的trie树上的点的最后一个点查,然而可能存在一个很长的串\((2e5)\)被划分了很多次\((2e5)\)的情况,就gg了。
这时候考虑原串只在对应的\(trie\)树上的点的最后一个产生贡献,那么统计答案的时候要算的是子树的和,于是化成\(dfs\)序的偏序问题,那么就多了一维偏序,\(cdq\)分治+树状数组即可。
效率\(O(nlog^2n)\),还有四倍的常数,好像有点垃圾,不过好在\(cdq\)分治和树状数组常数都不大。
吐槽
好久没写大数据结构了,感觉明明不是很难(应该说是一道中规中矩的数据结构题)却搞了半天,挺离谱的...
在调的时候主要的错误,一个是一开始没认真分析效率,TLE了还以为是被卡常,浪费了很久;然后还有诸如cdq分治最后一层忘记排序就返回之类的细节问题。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5,M=26;
int n,m,tp,tq,fp[N],fq[N];
struct node{
int t,x,u,d,p;
}p[N],q[N];
int to[N][M],dn[N],sz[N],pu[N],tt=1;
//trie
int dfs(int u){
sz[u]=1; dn[u]=++tt;
for(int i=0;i<M;i++) if(to[u][i]) sz[u]+=dfs(to[u][i]);
return sz[u];
}
//count(dn,sz)
bool cmp(node x,node y){
return x.t<y.t;
}
bool opt(node x,node y){
return x.x<y.x;
}
int su[N];
void add(int x,int y){
while(x<=tt) su[x]+=y,x+=x&-x;
}
int cnt(int x){
int s=0;
while(x) s+=su[x],x-=x&-x;
return s;
}
//binary tree
int qt,ans[N];
//count_ans
node pp[N],qq[N];
void work(int l,int r){
if(l==r){
//!!! remenber to finish what it should do before returning!!!
sort(p+fp[l],p+fp[r+1],opt);
sort(q+fq[l],q+fq[r+1],opt);
return;
}
int mid=(l+r)>>1;
work(l,mid); work(mid+1,r);
int j=fp[l];
for(int i=fq[mid+1];i<fq[r+1];i++){
while(j<fp[mid+1] && p[j].x<=q[i].x){
add(p[j].u,p[j].p);
j++;
}
ans[q[i].d]+=q[i].p*cnt(q[i].u);
}
for(int k=fp[l];k<j;k++) add(p[k].u,-p[k].p);
//count ans
for(int i=fp[l],tl=fp[l],tr=fp[mid+1];i<fp[r+1];i++){
if(tl<fp[mid+1] && (tr>=fp[r+1] || p[tl].x<=p[tr].x) ) pp[i]=p[tl++];
else pp[i]=p[tr++];
}
for(int i=fp[l];i<fp[r+1];i++) p[i]=pp[i];
for(int i=fq[l],tl=fq[l],tr=fq[mid+1];i<fq[r+1];i++){
if(tl<fq[mid+1] && (tr>=fq[r+1] || q[tl].x<=q[tr].x) ) qq[i]=q[tl++];
else qq[i]=q[tr++];
}
for(int i=fq[l];i<fq[r+1];i++) q[i]=qq[i];
//sort
}
char ch[N]; int id[N],lt[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%s",ch);
int l=strlen(ch),u=1;
for(int j=0;j<l;j++){
int x=ch[j]-'a';
if(!to[u][x]) to[u][x]=++tt;
u=to[u][x];
}
pu[i]=u; id[i]=i; lt[i]=0;
}
tt=0; dfs(1);
//init(s,n)
for(int i=1;i<=m;i++){
int op; scanf("%d",&op);
if(op==1){
int x,y; scanf("%d%d",&x,&y);
if(x==y) continue;
p[++tp]=(node){lt[id[x]],x,dn[pu[id[x]]],id[x],1};
p[++tp]=(node){i,x,dn[pu[id[x]]],id[x],-1};
lt[id[x]]=i;
p[++tp]=(node){lt[id[y]],y,dn[pu[id[y]]],id[y],1};
p[++tp]=(node){i,y,dn[pu[id[y]]],id[y],-1};
lt[id[y]]=i;
swap(id[x],id[y]);
}
else{
int k,l,r,u=1;
scanf("%s%d%d%d",ch,&k,&l,&r);
for(int j=0;j<k;j++) u=to[u][ch[j]-'a'];
qt++;
if(!u) continue;
q[++tq]=(node){i,r,dn[u]+sz[u]-1,qt,1};
q[++tq]=(node){i,r,dn[u]-1,qt,-1};
q[++tq]=(node){i,l-1,dn[u]+sz[u]-1,qt,-1};
q[++tq]=(node){i,l-1,dn[u]-1,qt,1};
}
}
for(int i=1;i<=n;i++) p[++tp]=(node){lt[id[i]],i,dn[pu[id[i]]],id[i],1};
//init(p,q,m)
sort(p+1,p+tp+1,cmp);
p[0].t=-1; p[tp+1].t=m+1;
for(int i=1;i<=tp+1;i++){
for(int j=p[i-1].t+1;j<=p[i].t;j++) fp[j]=i;
}
sort(q+1,q+tq+1,cmp);
q[0].t=-1; q[tq+1].t=m+1;
for(int i=1;i<=tq+1;i++){
for(int j=q[i-1].t+1;j<=q[i].t;j++) fq[j]=i;
}
work(0,m);
for(int i=1;i<=qt;i++) printf("%d\n",ans[i]);
return 0;
}
浙公网安备 33010602011771号