19 南京区域赛 F. Paper Grading

F. Paper Grading

题意:
2 e 5 2e5 2e5个字符串和 2 e 5 2e5 2e5个操作;
第一种操作是 交换两个字符串;
第二种是 给你 l , r , k l,r,k l,r,k 和一个字符串 s s s ,询问 区间 l 到 r l到r lr 中有多少个字符串的前 k k k个字符 和 s s s的前 k k k个字符相同。

思路:
先将所有字符串建成一颗 t r i e trie trie树,在字符串的结尾位置打个标记。
然后 询问下标 l 到 r l到r lr之间有多少个字符串 和 s s s的前 k k k 个字符相同,就转换成了 询问 t r i e trie trie树的一颗子树中 有多少个结束点 在 l 到 r l到r lr之间,询问一颗子树 可以用 d f s dfs dfs序 转换成询问区间的问题:那么操作二就转换成了经典的 求 L 到 R L到R LR l 到 r l到r lr 的和。还有操作一的修改操作,可以用树套树实现,就 O K OK OK 啦~
当然当然CDQ分治肯定也是没问题的啦~

o p s : ops: ops:数据结构没学好,每次遇到这种题,都会去想 带修可持久化,后来发现这个东西好像 子虚乌有;一般带修可持久化是用树套树来实现,即树状数组套动态开点线段树,支持的是单点修改。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;

int n,m;
int trie[200060][26];

int tot=1,cnt=0;
int ed[200060],dfn[200050],id[200050];
char s[200050];
struct node{
	int ls,rs,num;
}tr[30000050];
int siz[200050],root[200050];

void insert(int i){
	int now=1;
	int len=strlen(s);
	for(int i=0;i<len;i++){
		if(trie[now][s[i]-'a']);
		else {
			trie[now][s[i]-'a']=++tot;
		}
		now=trie[now][s[i]-'a'];
	}
	ed[i]=now;
}

void dfs1(int i){
	dfn[++cnt]=i;id[i]=cnt;
	siz[i]=1;
	for(int j=0;j<26;j++){
		if(trie[i][j])dfs1(trie[i][j]),siz[i]+=siz[trie[i][j]];
	}
}

void update(int &rt,int l,int r,int x,int w){
	if(!rt)rt=++tot;
	tr[rt].num+=w;
	if(l==r)return;
	int mid=l+r>>1;
	if(x<=mid)update(tr[rt].ls,l,mid,x,w);
	else update(tr[rt].rs,mid+1,r,x,w); 
}

int query(int rt,int l,int r,int x,int y){
	if(!rt)return 0;
	if(x<=l&&r<=y)return tr[rt].num;
	int mid=l+r>>1,ans=0;
	if(x<=mid)ans+=query(tr[rt].ls,l,mid,x,y);
	if(mid<y)ans+=query(tr[rt].rs,mid+1,r,x,y);
	return ans; 
}

int lowbit(int x){
	return x&(-x);
}

void add(int x,int pos,int w){
	while(x<=n){
		update(root[x],1,cnt,pos,w);
		x+=lowbit(x);
	}
}

int ask(int x,int l,int r){
	int ans=0;
	while(x){
		ans+=query(root[x],1,cnt,l,r);
		x-=lowbit(x);
	}
	return ans;
}

int get(int k){
	int now=1;
	for(int i=0;i<k;i++){
		if(trie[now][s[i]-'a'])now=trie[now][s[i]-'a'];
		else return 0;
	}
	return now;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",s);
		insert(i);
	}
	dfs1(1);
	tot=0;
	for(int i=1;i<=n;i++){
		add(i,id[ed[i]],1);
	}
	while(m--){
		int op;
		scanf("%d",&op);
		if(op==1){
			int a,b;scanf("%d%d",&a,&b);
			add(a,id[ed[a]],-1);
			add(a,id[ed[b]],1);
			add(b,id[ed[a]],1);
			add(b,id[ed[b]],-1);
			swap(ed[a],ed[b]);
		}else{
			int a,b,k;
			scanf("%s%d%d%d",s,&k,&a,&b);
			k=get(k);
			if(k==0)printf("0\n");
			else{
				printf("%d\n",ask(b,id[k],id[k]+siz[k]-1)-ask(a-1,id[k],id[k]+siz[k]-1));
			}
		}
	}
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值