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;
}
posted @ 2022-03-22 21:14  sz[sz]  阅读(184)  评论(0)    收藏  举报