2020CCPC(威海) - Caesar Cipher(线段树+哈希)

题目大意:给出一个长度为 n 的序列,接下来有 m 次操作,每次操作分为下列两种类型:

  1. 1 l r:区间 [ l , r ] 内的所有数都加 1 并对 65536 取模,也就是 i ∈ [ l , r ] ,有 a[ i ] =( a[ i ] + 1 ) % 65536
  2. 2 x y len:查询两段区间 [ x , x + len - 1 ] 和 [ y , y + len - 1 ] 内的序列是否相同

题目分析:第一种操作可以转化成区间更新,第二种操作可以转换成区间查询和哈希,所以不难想到使用线段树去维护每个位置的加权哈希值,这样就可以轻松更新和访问了

如果将操作一中的对 65536 取模删除掉,这就是一个非常简单的问题了,首先设置权值:f[ 1 ] = base,f[ 2 ] = base^2 ... f[ i ] = base^i,每个叶子节点的权值就是 val[ i ] * f[ i ] ,每次区间更新也就是 [ l , r ] 这段区间加上 f[ l ] + f[ l + 1 ] + ... + f[ r ] ,如此递归下去即可

那么操作一加上了对 65536 取模会有什么影响呢?有一些数字从 65535 加一变成 65536 之后,包含这些数字的区间 [ l , r ] 就不再是单纯的加上 f[ l ] + f[ l + 1 ] + ... + f[ r ] 了,还需要特殊处理从 65535 变成 65536 的这些位置,因为 65536 取模后就换成 0 了

然后是一个比较简单的思维点了,我们称一个数字从 65535 加到 65536 为溢出,因为整个序列的长度为 n ,总共的操作次数为 m,所以最多会出现 n * m / 65536 次溢出,估算一下是 5e6 级别的,对于这些溢出,我们完全可以单独处理

到此为止直接写线段树就可以了,线段树中多维护一个最大值用来暴力维护溢出的情况,总的时间复杂度为:O( n*q*logn/65536 + qlogn )

然后出题人昨天也表明了,这个题卡了自然溢出,所以需要自己设置一个基数和模数用来哈希

代码:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
     
typedef long long LL;
     
typedef unsigned long long ull;
     
const int inf=0x3f3f3f3f;

const int N=5e5+100;

const int base=2333333;

const int mod=999999998;

LL f[N];

struct Node
{
	int l,r,len;
	LL base,Hash,mmax,lazy;
}tree[N<<2];

void pushup(int k)
{
	tree[k].Hash=(tree[k<<1].Hash+tree[k<<1|1].Hash)%mod;
	tree[k].mmax=max(tree[k<<1].mmax,tree[k<<1|1].mmax);
}

void pushdown(int k)
{
	if(tree[k].lazy)
	{
		LL lz=tree[k].lazy;
		tree[k].lazy=0;
		tree[k<<1].Hash=(tree[k<<1].Hash+tree[k<<1].base*lz)%mod;
		tree[k<<1|1].Hash=(tree[k<<1|1].Hash+tree[k<<1|1].base*lz)%mod;
		tree[k<<1].mmax+=lz;
		tree[k<<1|1].mmax+=lz;
		tree[k<<1].lazy+=lz;
		tree[k<<1|1].lazy+=lz;
	}
}

void build(int k,int l,int r)
{
	tree[k].l=l;
	tree[k].r=r;
	tree[k].len=r-l+1;
	tree[k].lazy=0;
	if(l==r)
	{
		scanf("%lld",&tree[k].mmax);
		tree[k].base=f[l];
		tree[k].Hash=tree[k].base*tree[k].mmax%mod;
		return;
	}
	int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	tree[k].base=(tree[k<<1].base+tree[k<<1|1].base)%mod;
	pushup(k);
}

void update(int k,int l,int r)
{
	if(tree[k].r<l||tree[k].l>r)
		return;
	if(tree[k].l>=l&&tree[k].r<=r)
	{
		tree[k].Hash=(tree[k].Hash+tree[k].base)%mod;
		tree[k].mmax++;
		tree[k].lazy++;
		return;
	}
	pushdown(k);
	update(k<<1,l,r);
	update(k<<1|1,l,r);
	pushup(k);
}

void update_mod(int k)//记录下来所有最大值为65536的位置单独更新 
{
	if(tree[k].mmax<65536)
		return;
	if(tree[k].l==tree[k].r)
	{
		tree[k].mmax=0;
		tree[k].Hash=0;
		return;
	}
	pushdown(k);
	update_mod(k<<1);
	update_mod(k<<1|1);
	pushup(k);
}

LL query(int k,int l,int r)
{
	if(tree[k].r<l||tree[k].l>r)
		return 0;
	if(tree[k].l>=l&&tree[k].r<=r)
		return tree[k].Hash;
	pushdown(k);
	return (query(k<<1,l,r)+query(k<<1|1,l,r))%mod;
}

bool check(int x,int y,int len)
{
	if(x>y)
		swap(x,y);
	LL ans1=query(1,x,x+len-1)*f[y-x]%mod;
	LL ans2=query(1,y,y+len-1);
	return ans1==ans2;
}

void init()
{
	f[0]=1;
	for(int i=1;i<N;i++)
		f[i]=f[i-1]*base%mod;
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	init();
	int n,m;
	scanf("%d%d",&n,&m);
	build(1,1,n);
	while(m--)
	{
		int op;
		scanf("%d",&op);
		if(op==1)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			update(1,l,r);
			update_mod(1);
		}
		else
		{
			int x,y,len;
			scanf("%d%d%d",&x,&y,&len);
			printf("%s\n",check(x,y,len)?"yes":"no");
		}
	}












    return 0;
}


 

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/109291278
今日推荐