zoukankan      html  css  js  c++  java
  • [Luogu4715]「英语」Z 语言

    luogu

    description

    你有一个长度为(n)的串(A)和一个长度为(m)的串(B),字符集大小(2^{31}),且同一个串中没有相同的元素。
    定义(B)串与(A_{i...i+m-1})匹配((1le i le n-m+1)),当且仅当(B)串对应的排列与(A_{i...i+m-1})对应的排列完全相同。
    一个串对应的排列就是把串的每个元素的值替换成它在这个串中从小到大的(Rank)。由于不存在相同的元素,所以这样子一定会得到一个排列。
    现在要动态修改(B)串每个位置的元素,并询问每次修改后(B)串在(A)串中的出现次数。
    (n,m,qle10^5)

    sol

    由于(A)串是固定的,我们可以先求出(A)串中每个长为(m)的子串的(hash)值,然后只要动态维护出(B)串的(hash)值,丢到一个(map)里面去找就行了。
    但是这个(hash)值的计算方法需要只与相对大小有关而与具体数值无关。
    我们可以对串维护一棵平衡树,平衡树的键值是串中的数值,并在树上的每个位置维护节点在原串中的下标。
    这样两个串匹配就当且仅当他们构出来的平衡树的先序遍历得到的下标序列完全相同。
    于是就只需要对这个下标维护一个(hash)值即可。
    对于(A_{i...i-m+1},i>1)的这些子串,下标并不是从(1)开始编号,但是可以看作是每个下标都加上了一个固定的常数,那么最终得出来的(hash)也就加上了一个可计算得到的常数,减去即可。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<map>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    #define ull unsigned long long
    const int N = 2e5+5;
    const ull base = 20020415;
    int n,m,q,a[N],b[N];ull pw[N],delta;
    map<ull,int>M;
    struct Splay{
    	int fa[N],ch[2][N],sz[N],val[N],pos[N],root,tot;ull hsh[N];
    	bool son(int x){return x==ch[1][fa[x]];}
    	void pushup(int x){
    		sz[x]=sz[ch[0][x]]+1+sz[ch[1][x]];
    		hsh[x]=hsh[ch[0][x]]*pw[sz[ch[1][x]]+1]+pos[x]*pw[sz[ch[1][x]]]+hsh[ch[1][x]];
    	}
    	void rotate(int x){
    		int y=fa[x],z=fa[y],c=son(x);
    		ch[c][y]=ch[c^1][x];if (ch[c][y]) fa[ch[c][y]]=y;
    		fa[x]=z;if (z) ch[son(y)][z]=x;
    		ch[c^1][x]=y;fa[y]=x;pushup(y);
    	}
    	void splay(int x,int goal){
    		for (int y=fa[x];y!=goal;rotate(x),y=fa[x])
    			if (fa[y]!=goal) son(x)^son(y)?rotate(x):rotate(y);
    		pushup(x);if (!goal) root=x;
    	}
    	void insert(int v,int p){
    		if (!root){
    			root=++tot;val[tot]=v,pos[tot]=p;
    			pushup(root);return;
    		}
    		int x=root;
    		while (1)
    			if (v<val[x])
    				if (!ch[0][x]){
    					ch[0][x]=++tot;val[tot]=v,pos[tot]=p,fa[tot]=x;
    					splay(tot,0);return;
    				}else x=ch[0][x];
    			else
    				if (!ch[1][x]){
    					ch[1][x]=++tot;val[tot]=v,pos[tot]=p,fa[tot]=x;
    					splay(tot,0);return;
    				}else x=ch[1][x];
    	}
    	int suf(){
    		int x=ch[1][root];
    		while (ch[0][x]) x=ch[0][x];
    		splay(x,root);return x;
    	}
    	void erase(int v){
    		int x=root;
    		while (1)
    			if (v<val[x]) x=ch[0][x];
    			else if (v>val[x]) x=ch[1][x];
    			else break;
    		splay(x,0);
    		if (!ch[1][x]) {fa[root=ch[0][x]]=0;return;}
    		int y=suf();fa[ch[0][x]]=y;ch[0][y]=ch[0][x];fa[root=y]=0;
    	}
    }A,B;
    int main(){
    	n=gi();m=gi();q=gi();pw[0]=1;
    	for (int i=1;i<N;++i) pw[i]=pw[i-1]*base;
    	for (int i=0;i<m;++i) delta+=pw[i];
    	for (int i=1;i<=n;++i) a[i]=gi();
    	for (int i=1;i<=m;++i) b[i]=gi();
    	for (int i=1;i<=m;++i) A.insert(a[i],i);++M[A.hsh[A.root]];
    	for (int i=m+1;i<=n;++i){
    		A.erase(a[i-m]);A.insert(a[i],i);
    		++M[A.hsh[A.root]-delta*(i-m)];
    	}
    	for (int i=1;i<=m;++i) B.insert(b[i],i);
    	while (q--){
    		int p=gi();B.erase(b[p]);
    		b[p]=gi();B.insert(b[p],p);
    		printf("%d
    ",M[B.hsh[B.root]]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    [Linux]Vmwaer创建CENTOS7虚拟机[转]
    [游记]二访金陵
    [Android]ADB调试: SecurityException: Injecting to another application requires INJECT_EVENTS permission
    [操作系统]记一次未尽的三星 Galaxy A6s(SM-G6200)刷机过程
    [网络]NAT与内网穿透技术初探【待续】
    结构化系统建模之程序流程图|系统流程图|数据流图
    UML系统建模之用例视图
    [Linux]常用命令之【mkdir/touch/cp/rm/ls/mv】
    [Linux]监控外部用户登录及外部主机连接情况
    [Java SE]Unicode解码
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/9245622.html
Copyright © 2011-2022 走看看