BZOJ 3439 Kpm的MC密码 Trie树+可持久化线段树

题目大意:给定n个字符串,对于每个字符串求以这个字符串为后缀的字符串中第k小的编号

首先将字符串反转 那么就变成了对于每个字符串求以这个字符串为前缀的字符串中第k小的编号

然后考虑对字符串排序 那么对于每个字符串以它为前缀的字符串一定是连续的 那么就转化成了区间第k小 这个用可持久化线段树可以解决

排序自然不能直接排 既然是字符串 考虑Trie树+DFS即可 注意字符串有重复的 小心

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
struct Trie{
	vector<int> num;
	Trie *son[26];
	void* operator new (size_t size);
}*root,*mempool,*C;
struct Segtree{
	Segtree *ls,*rs;
	int num;
	void* operator new (size_t size,Segtree *_,Segtree *__,int ___);
}*tree[M],*_mempool,*G;
int n,a[M],l[M],r[M],tot;
char s[M];
void* Trie :: operator new (size_t size)
{
	if(C==mempool)
	{
		C=new Trie[1<<15];
		mempool=C+(1<<15);
		memset(C,0,sizeof(Trie)*(1<<15) );
	}
	return C++;
}
void* Segtree :: operator new (size_t size,Segtree *_,Segtree *__,int ___)
{
	if(G==_mempool)
	{
		G=new Segtree[1<<15];
		_mempool=G+(1<<15);
		memset(G,0,sizeof(Segtree)*(1<<15) );
	}
	G->ls=_;
	G->rs=__;
	G->num=___;
	return G++;
}
void Insert(Trie*&p,char *str,int pos)
{
	if(!p) p=new Trie;
	if(!*str)
	{
		p->num.push_back(pos);
		return ;
	}
	Insert(p->son[(*str)-'a'],str+1,pos);
}
void DFS(Trie *p)
{
	int i;
	vector<int>::iterator it;
	p->num.begin();
	int temp=tot+1;
	for(it=p->num.begin();it!=p->num.end();it++)
	{
		a[++tot]=*it;
		l[*it]=temp;
	}
	for(i=0;i<26;i++)
		if(p->son[i])
			DFS(p->son[i]);
	for(it=p->num.begin();it!=p->num.end();it++)
		r[*it]=tot;
}
Segtree* Build_Tree(Segtree *p,int x,int y,int val)
{
	int mid=x+y>>1;
	if(x==y) return new (0x0,0x0,p->num+1) Segtree;
	if(val<=mid) return new (Build_Tree(p->ls,x,mid,val),p->rs,p->num+1) Segtree;
	else 		 return new (p->ls,Build_Tree(p->rs,mid+1,y,val),p->num+1) Segtree;
}
int Get_Ans(Segtree *p1,Segtree *p2,int x,int y,int k)
{
	int mid=x+y>>1;
	if(x==y) return mid;
	int temp=p2->ls->num-p1->ls->num;
	if(k<=temp) return Get_Ans(p1->ls,p2->ls,x,mid,k);
	else return Get_Ans(p1->rs,p2->rs,mid+1,y,k-temp);
}
int main()
{
	int i,j,k;
	cin>>n;
	for(i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		int temp=strlen(s+1);
		for(j=1;j<<1<=temp;j++)
			swap(s[j],s[temp+1-j]);
		Insert(root,s+1,i);
	}
	DFS(root);
	tree[0]=new (0x0,0x0,0) Segtree;
	tree[0]->ls=tree[0]->rs=tree[0];
	for(i=1;i<=n;i++)
		tree[i]=Build_Tree(tree[i-1],1,n,a[i]);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&k);
		if(k>r[i]-l[i]+1) puts("-1");
		else printf("%d\n", Get_Ans(tree[l[i]-1],tree[r[i]],1,n,k) );
	}
}
时间: 2024-10-07 16:06:28

BZOJ 3439 Kpm的MC密码 Trie树+可持久化线段树的相关文章

BZOJ 3439: Kpm的MC密码( trie + DFS序 + 主席树 )

把串倒过来插进trie上, 那么一个串的kpm串就是在以这个串最后一个为根的子树, 子树k大值的经典问题用dfs序+可持久化线段树就可以O(NlogN)解决 ------------------------------------------------------------------ #include<cstdio> #include<cstring> #include<algorithm> #include<cctype> using namespa

BZOJ 3439 Kpm的MC密码 Trie+可持久化线段树

题目大意:定义一种串,如果一个串是另一个串的后缀,那么这个串称作kpm串.问一个串的标号第k大的kpm串是多少. 思路:将所有的串翻转之后变成前缀,全都插进一个Trie树中.每个节点维护一个last指针,表示最后一次更新的可持久化线段树的指针,如果再有串经过这里,就继续更新last指针.最后只需要查询last指针中的东西就可以了. CODE: #include <cstdio> #include <cstring> #include <iostream> #includ

BZOJ 3439 Kpm的MC密码

倒着建trie,然后主席树来求子树第k大. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define maxn 300500 using namespace std; int n,trie[maxn][27],fath[maxn],cnt=0,tag[maxn],k,rot,kr,dfn[maxn],mx[m

主席树/函数式线段树/可持久化线段树

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

主席树 | | 可持久化线段树

可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 所以这里讲的可持久化线段树也叫函数式线段树(又叫主席树……因为先驱就是fotile主席Orz……). 先了解一下主席树 http://seter.is-programmer.com/posts/31907.html    很详细的介绍了函数式线段树(主席树). 主席树其实就是很多棵线段树,由于每次更新只需要更新logN个节点,所

[BZOJ 3207] 花神的嘲讽计划Ⅰ【Hash + 可持久化线段树】

题目链接:BZOJ - 3207 题目分析 先使用Hash,把每个长度为 k 的序列转为一个整数,然后题目就转化为了询问某个区间内有没有整数 x . 这一步可以使用可持久化线段树来做,虽然感觉可以有更简单的做法,但是我没有什么想法... 代码 #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #inclu

BZOJ 2527 Poi2011 Meteors 整体二分+线段树 / 可持久化线段树(MLE)

题目大意:给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值 首先我们考虑暴力想法 对于每个国家分开讨论 二分操作次数 但是这样每次Judge的时候我们要模拟1~mid所有的操作 浪费在这里的复杂度实在太大 这样做每个国家需要模拟O(klogk)次操作 时间复杂度O(nklogk) TLE 我们需要对浪费在这里的复杂度做一些改进 1.可持久化线段树(MLE) 每次二分一个mid之后 我们要找到mid次

BZOJ 3207 花神的嘲讽计划I Hash+可持久化线段树

题目大意:给定一个数字串,多次求某个区间内有没有一个长度为k的子串 首先对字符串进行哈希 然后问题就转化成了求一个区间内有没有某个数 可持久化线段树即可 其实我觉得划分树会更快一些 可以写写 ※注意事项: 1.n<=200000 我找不到数据范围是眼科大夫去找老阎的关系? 2.哈希值用unsigned long long 铁则 unsigned int 会被卡掉 3.线段树那里直接x+y>>1会爆unsigned long long 转换一下 x+y>>1=(x>>

BZOJ 3551 ONTAK2010 Peaks加强版 Kruskal重构树+可持久化线段树

题目大意:同3545 强制在线 3545题解传送门:http://blog.csdn.net/popoqqq/article/details/40660953 强制在线没法排序 启发式合并也就用不了了 Kruskal重构树是个挺好玩的东西 可以拿来处理一些最小生成树的边权最值问题 这里我们Kruskal连边时并不直接连边 而是新建一个节点ext 将两个点所在子树都连到ext的儿子上 比如说样例的树就建成了这样 图中红色的是原图的边权,黑色的是原图上的点 这样生成的树有一些十分优美的性质: 1.二