【BZOJ3439】Kpm的MC密码 Trie+dfs序+可持久化线段树

题解:

咳咳。首先补全数据范围:对于100%的数据,1<=n<=100000,0<len<=300000

然后乍一看题(BZ少数据范围),直接排个序,然后插入点信息来一发可持久化线段树求区间第K大、

显然存不下,那么我们可以用Trie存一下这些字符串,然后dfs序扫一遍,确定一个字符串的可查询范围。


话说用Trie树存的是反串(后缀么)

呃,今天生病了,有点犯浑,语文能力可能下降了许多。。

所以还是看代码吧。


完了我已经病入膏肓,可持久化线段树空间开O(n)还好几遍都觉得“并没有错”!


代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define LOGN 20
#define M 301000
#define T 26
#define ls son[x][0]
#define rs son[x][1]
using namespace std;
int e[N],head[M],cnt;
inline void add(int u)
{
	e[++cnt]=head[u];
	head[u]=cnt;
}

int n,L[N],R[N];
struct Functional_Tree
{
	int root[N*LOGN],son[N*LOGN][2],size[N*LOGN],cnt,Last;
	int insert(int last,int l,int r,int p)
	{
		int x=++cnt;
		if(l==r){size[x]=1;return x;}
		int mid=l+r>>1;
		if(p<=mid)ls=insert(son[last][0],l,mid,p),rs=son[last][1];
		else rs=insert(son[last][1],mid+1,r,p),ls=son[last][0];
		size[x]=size[ls]+size[rs];
		return x;
	}
	void Insert(int id)
	{
		root[Last+1]=insert(root[Last],1,n,id);
		Last++;
	}
	int query(int last,int now,int l,int r,int k)
	{
		int mid=l+r>>1;
		if(size[now]-size[last]<k)return -1;
		if(l==r)return l;
		int temp=size[son[now][0]]-size[son[last][0]];
		if(temp>=k)return query(son[last][0],son[now][0],l,mid,k);
		else return query(son[last][1],son[now][1],mid+1,r,k-temp);
	}
	int Query(int i,int k){return query(root[L[i]],root[R[i]],1,n,k);}
}fct;

struct Trie
{
	int next[M][T],cnt,tot;
	char s[M];
	void insert()
	{
		int i,x=0,alp,len;
		scanf("%s",s),len=strlen(s);
		for(i=len-1;i>=0;i--)
		{
			alp=s[i]-'a';
			if(!next[x][alp])next[x][alp]=++cnt;
			x=next[x][alp];
		}
		add(x);
	}
	void dfs(int x)
	{
		int i,v,rem=tot;
		for(i=head[x];i;i=e[i])
		{
			tot++;
			fct.Insert(i);
		}
		for(i=0;i<26;i++)if(v=next[x][i])dfs(v);
		for(i=head[x];i;i=e[i])L[i]=rem,R[i]=tot;
	}
}trie;

int main()
{
	freopen("test.in","r",stdin);
	int i,j,k;
	for(scanf("%d",&n),i=1;i<=n;i++)trie.insert();
	trie.dfs(0);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&k);
		printf("%d\n",fct.Query(i,k));
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值