FZU 2280 Magic

给定n个字符串,每个字符串一个价值wi,询问

1  更改某个串的价值

2 查询以第x个串为后缀且价值小等于x串的字符串个数

字符串个数 n<=1000

查询数 q<=80000

分析:根据字符串倒序构建字典树,那么对于某个字符串,以它为后缀的字符串在该字典树中成为了它的子树。然后根据字典树构建dfs序,查询相当于dfs序的区间查询。

算法一: 使用hash预处理出树,然后构建dfs序,分块进行查询和更新。

hash预处理n^2,查询sqrt(n),更新sqrt(n),总时间复杂度:O(q*sqrt(n))

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define pri 233
#define M 1000000007
typedef long long ll;

using namespace std;


struct BlockQuery{
	int num[35][35][35];
	int block,cnt;
	int digit[1010];
	BlockQuery(int n){
		int i=1;
		for (;i*i<n;i++);
		block=i;
		cnt=(n-1)/block+1;
		memset(num,0,sizeof(num));
	}
	void add(int pos,int val,int f){
		int loc=pos/block;
		int valsq = val/33;
		digit[pos] = val;
		for (int i=valsq+1;i<=34;i++)
			num[loc][i][34] += f;
		int mval = val%33;
		for (int i=mval;i<=34;i++)
			num[loc][valsq][i] += f;
	}
	int getLeq(int l,int r,int val){
		int lb=l/block,rb=r/block;
		int lbr = lb*block+block-1;
		int sum=0;
		if (lb!=rb){
			for (int i=l;i<=lbr;i++)
				sum+= (digit[i]<=val)?1:0;
			for (int i=rb*block;i<=r;i++)
				sum+= (digit[i]<=val)?1:0;
			for (int i=lb+1;i<rb;i++){
				int valsq = val/33;
				if (valsq>0)
					sum+= num[i][valsq-1][34];
				sum+= num[i][valsq][val%33];
			}
		}
		else
		{
			for (int i=l;i<=r;i++)
				sum += digit[i]<=val?1:0;
		}
		
		return sum;
	}
};

char st[1010];
int l[1010],r[1010];

struct node{
	char st[1010];
	int hash[1010];
	int len,id,w,color;
} a[1010];
bool cmp0(const node &a,const node &b){
	return a.len>b.len;	
}

int powt[1010];
int getHashValue(int *h,int l,int r){
	return (h[r]-(ll)h[l-1]*powt[r-l+1]%M+M)%M;	
}

int tot,Link[1010];
struct node2{
	int v,next;	
}	edge[10100];
void addEdge(int x,int y){
	tot++;
	edge[tot].v=y;
	edge[tot].next=Link[x];
	Link[x]=tot;	
}

bool vis[1010];
int tim;
int L[1010],R[1010],Loc[1010];
void dfs(int x){
	vis[x]=true;
	tim++;
	Loc[a[x].id]=L[a[x].id]=tim;
	for (int p=Link[x];p;p=edge[p].next)
		dfs(edge[p].v);
	R[a[x].id]=tim;
}
int main(){
	powt[0]=1;
	for (int i=1;i<1010;i++)
		powt[i]=(ll)powt[i-1]*pri%M;
	
	int T;
	scanf("%d",&T);
	while (T-->0){
		int n;
		scanf("%d",&n);
		BlockQuery bq(n);
		
		for (int i=1;i<=n;i++){
			scanf("%s %d",a[i].st+1,&a[i].w);
			a[i].id=i;
			a[i].len=strlen(a[i].st+1);
			a[i].hash[0]=0;
			for (int j=1;j<=a[i].len;j++)
				a[i].hash[j]=((ll)a[i].hash[j-1]*pri+a[i].st[j])%M;
		}
		sort(a+1,a+1+n,cmp0);
		
		tot=0;
		memset(Link,0,sizeof(Link));
		for (int i=1;i<=n;i++){
			a[i].color = a[i].id;
			for (int j=i+1;j<=n;j++)
			{
				if (getHashValue(a[j].hash,1,a[j].len)==
					getHashValue(a[i].hash,a[i].len-a[j].len+1,a[i].len)){
					a[i].color = a[j].id;
					addEdge(j,i);
					break;
				}
			}
		}
		
		tim=0;
		memset(vis,0,sizeof(vis));
		for (int i=1;i<=n;i++)
			if (!vis[i]&&a[i].color==a[i].id)
				dfs(i);
				
		for (int i=1;i<=n;i++)
			for (int j=n;j>i;j--)
				if (a[i].len==a[j].len&&a[i].hash[a[i].len]==a[j].hash[a[i].len]){
					L[a[i].id] = L[a[j].id];
					R[a[i].id] = R[a[j].id];
					break;	
				}
			
		for (int i=1;i<=n;i++)
			bq.add(Loc[a[i].id],a[i].w,1);

		int q,choose,x,y;
		scanf("%d",&q);
		for (int i=1;i<=q;i++){
			scanf("%d",&choose);
			if (choose==1){
				scanf("%d%d",&x,&y);
				bq.add(Loc[x],bq.digit[Loc[x]],-1);
				bq.add(Loc[x],y,1);
			}
			else{
				scanf("%d",&x);	
				printf("%d\n",bq.getLeq(L[x],R[x],bq.digit[Loc[x]]));
			}
		}
	}
	
	return 0;	
}



算法二: 建立字典树,构建dfs序。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值