正题
题目链接:https://www.luogu.com.cn/problem/P6688
解题思路
nnn个数,每次有操作
- 修改一个数
- 询问两个区间是否他们中的元素分别组成的可重集合A,BA,BA,B,满足对于每个Ai=Bi+kA_i=B_i+kAi=Bi+k其中kkk是一个相同的数
解题思路
先不考虑kkk的问题
我们字符串hashhashhash的特征值是第iii位为pip^ipi,但是我们这边并不考虑它的顺序,然后可重集合提示我们要构造集合的特征值。那么我们对于一个数xxx它的特征值是pxp^xpx,若一个集合中有cic_ici个iii,那么这个集合的特征值就是∑i=1∞pici\sum_{i=1}^{\infty}p^ic_i∑i=1∞pici即可。
如果考虑kkk怎么做,那么满足条件的话就是满足AiA_iAi中的每个数都比BiB_iBi多kkk,所以我们只需要求出A,BA,BA,B的元素和的差,然后除以它们的大小就是kkk了,然后小的那个特征值乘上一个pkp^kpk比较即可。
线段树维护区间特征值和区间和,因为卡了自然溢出需要自己设定模数,时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define ull unsigned long long
using namespace std;
const ll N=1e6+10;
const ull p=1145141,P=1e9+7;
ull pw[N],w[N<<2];
ll n,q,v[N<<2],a[N];
void Build(ll x,ll l,ll r){if(l==r){w[x]=pw[a[l]];v[x]=a[l];return;} ll mid=(l+r)>>1;Build(x*2,l,mid);Build(x*2+1,mid+1,r);w[x]=(w[x*2]+w[x*2+1])%P;v[x]=v[x*2]+v[x*2+1];return;
}
void Change(ll x,ll l,ll r,ll pos,ll val){if(l==r){w[x]=pw[val];v[x]=val;return;}ll mid=(l+r)>>1;if(pos<=mid)Change(x*2,l,mid,pos,val);else Change(x*2+1,mid+1,r,pos,val);w[x]=(w[x*2]+w[x*2+1])%P;v[x]=v[x*2]+v[x*2+1];return;
}
void Ask(ll x,ll L,ll R,ll l,ll r,ull &sw,ll &sv){if(L==l&&R==r){(sw+=w[x])%=P;sv+=v[x];return;}ll mid=(L+R)>>1;if(r<=mid)Ask(x*2,L,mid,l,r,sw,sv);else if(l>mid)Ask(x*2+1,mid+1,R,l,r,sw,sv);else Ask(x*2,L,mid,l,mid,sw,sv),Ask(x*2+1,mid+1,R,mid+1,r,sw,sv);return;
}
int main()
{scanf("%lld%lld",&n,&q);pw[0]=1;for(ll i=1;i<=1e6;i++)pw[i]=pw[i-1]*p%P;for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);Build(1,1,n);while(q--){ll op;scanf("%lld",&op);if(op==0){ll x,y;scanf("%lld%lld",&x,&y);Change(1,1,n,x,y);}else{ll l0,r0,l1,r1,sv0=0,sv1=0;ull sw0=0,sw1=0;scanf("%lld%lld%lld%lld",&l0,&r0,&l1,&r1);Ask(1,1,n,l0,r0,sw0,sv0);Ask(1,1,n,l1,r1,sw1,sv1);if(sv1<sv0)swap(sv0,sv1),swap(sw0,sw1);if((sv1-sv0)%(r1-l1+1)){printf("NO\n");continue;}ll k=(sv1-sv0)/(r1-l1+1);sw0=sw0*pw[k]%P;if(sw0==sw1)printf("YES\n");else printf("NO\n");}}return 0;
}