1. 程式人生 > 其它 >The 2019 ICPC Asia Nanjing Regional Contest F Paper Grading

The 2019 ICPC Asia Nanjing Regional Contest F Paper Grading


[題面](https://codeforces.com/gym/103466/problem/F)

# 思路

看到網上都寫樹套樹?就我直觀的想法是離線然後$cdq$嗎...

發現比較麻煩的是那個交換操作,考慮對詢問離線,那麼每個原串對答案的貢獻就被交換操作分為$O(m)$個在一個時間段上的貢獻。把原串的時間段和詢問的下標區間都分為“後減前”這兩段,轉化為二維偏序問題。

考慮如何處理lcp的條件限制,原本以為可以直接對原串暴力在trie樹上跑一遍產生貢獻,詢問直接在串對應的trie樹上的點的最後一個點查,**然而可能存在一個很長的串$(2e5)$被劃分了很多次$(2e5)$的情況,就gg了**。

這時候考慮原串只在對應的$trie$樹上的點的最後一個產生貢獻,那麼統計答案的時候要算的是子樹的和,於是化成$dfs$序的偏序問題,那麼就多了一維偏序,$cdq$分治+樹狀陣列即可。

效率$O(nlog^2n)$,還有四倍的常數,好像有點垃圾,不過好在$cdq$分治和樹狀陣列常數都不大。

# 吐槽
好久沒寫大資料結構了,感覺明明不是很難(應該說是一道中規中矩的資料結構題)卻搞了半天,挺離譜的...
在調的時候主要的錯誤,一個是一開始沒認真分析效率,TLE了還以為是被卡常,浪費了很久;然後還有諸如cdq分治最後一層忘記排序就返回之類的細節問題。

# 程式碼

```cpp
#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;
}
```