zoukankan      html  css  js  c++  java
  • cf213E 线段树维护hash

    链接

    https://codeforces.com/contest/213/problem/E

    题目大意

    给出两个排列a、b,长度分别为n、m,你需要计算有多少个x,使
    (a_1 + x; a_2 + x; a_3 + x、、、 a_n + x) 是b 的子序列(不连续的那种)。

    思路

    巧妙啊
    暴力直接扫会T
    我们构造一个c数组,使得c[b[i]]=i
    这样x+1到x+1+n就是一段连续的区间了233
    插回去看看他们相对大小是不是和a数组相同
    因为不连续所以线段树维护hash值,线段树按照c数组的原路插回去
    查询这一段是否hash值相同
    ؏؏☝ᖗ乛◡乛ᖘ☝؏؏
    不知道双进制有没有用

    update 19.3.14

    写的稍微有点不清楚
    因为a数组是个排列,所以[1,n]总的范围是1到n无重复
    这样我们拿b数组开刀,c[b[i]]=i就是下标存值,值存下标。
    这样你把他们放回去区间长度为n的下标,和a数组下标比较一下相对大小。
    看看是否相同就可以判断是否是子串了。
    然后[1,m],[2,m+1],[3,m+2]挨着扫就行了。

    错误

    报错:你数组开小啦
    我:ull炸了吗?

    代码

    #include <iostream>
    #include <cstdio>
    #define ull unsigned long long
    using namespace std;
    const int N=2e5+7;
    const int mod[2]={233,1000000009};
    int read() {
    	int x=0,f=1;char s=getchar();
    	for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
    	for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
    	return x*f;
    }
    int n,m,pos[N],a[N],b[N];
    ull my_pow[2][N];
    namespace seg {
    	#define ls rt<<1
    	#define rs rt<<1|1
    	struct node {
    		int siz;
    		ull tot[2];
    	}e[N<<2];
    	int cnt;
    	void pushup(int rt) {
    		e[rt].siz=e[ls].siz+e[rs].siz;
    		e[rt].tot[0]=(e[ls].tot[0]*my_pow[0][e[rs].siz+1]+e[rs].tot[0]);
    		e[rt].tot[1]=(e[ls].tot[1]*my_pow[1][e[rs].siz+1]+e[rs].tot[1]);
    	}
    	void update(int l,int r,int id,int k,int rt) {
    		if(l==r) {
    			if(k==-1) e[rt].siz=e[rt].tot[0]=e[rt].tot[1]=0;
    			else e[rt].siz=1,e[rt].tot[0]=e[rt].tot[1]=k;
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(id<=mid) update(l,mid,id,k,ls);
    		else update(mid+1,r,id,k,rs);
    		pushup(rt);
    	}
    }
    int main() {
    	n=read(),m=read();
    	my_pow[1][1]=my_pow[0][1]=1;
    	for(int i=2;i<=n+1;++i) {
    		my_pow[0][i]=my_pow[0][i-1]*mod[0];
    		my_pow[1][i]=my_pow[1][i-1]*mod[1];
    	}
    	for(int i=1;i<=n;++i) a[i]=read();
    	for(int i=1;i<=m;++i) b[i]=read();
    	ull hash[2]={0,0},tmp[2]={0,0};
    	for(int i=1;i<=n;++i) {
    		hash[0]=(hash[0]*mod[0]+a[i]);
    		hash[1]=(hash[1]*mod[1]+a[i]);
    		tmp[0]=(tmp[0]*mod[0]+1);
    		tmp[1]=(tmp[1]*mod[1]+1);
    	}
    	for(int i=1;i<=m;++i) pos[b[i]]=i;
    	int ans=0;
    	for(int i=1;i<=m;++i) {
    		if(i>n) seg::update(1,m,pos[i-n],-1,1);
    		seg::update(1,m,pos[i],i,1);
    		if(i>=n&&seg::e[1].tot[0]==(tmp[0]*(i-n)+hash[0])&&
    			     seg::e[1].tot[1]==(tmp[1]*(i-n)+hash[1])) ans++;
    	}
    	cout<<ans<<"
    ";
    	return 0;
    }
    
  • 相关阅读:
    支付宝-单笔转账接口
    # Creating Server TCP listening socket *:6379: bind: No such file or directory
    Window 下安装 Redis,配置redis环境变量
    Ajax的跨域(一)
    web人脸识别(二)
    web人脸识别(一)
    给GridView添加列头复选框
    计算两个时间相差多少年月日的sql算法
    MUI下拉加载安卓手机无效的解决方法
    博主回来了!
  • 原文地址:https://www.cnblogs.com/dsrdsr/p/10411429.html
Copyright © 2011-2022 走看看