先来说一下傻逼的做法。
考虑如何约束等差数列这个条件,如果k=0,就是[最大值=最小值];否则就是区间中[相邻两数差的绝对值的gcd=k][(最大值-最小值)/(r-l)=k][区间中没有相同元素]。
gcd可以在 O(log2n) 的时间复杂度很容易搞出来,所以问题就在于怎么确定区间中没有相同元素。
显然,如果记每个位置下一个和它权值相同的位置在哪,那就转化成了求区间最小值的问题。而这个玩意儿其实就是求后继。本来想用主席树求,但是mle了。。所以就写了个平衡树。正好学一下treap。。
treap竟然可以不保存父指针,还是有点厉害的。。插入和删除感觉其实跟普通的二叉堆差不多。就是要善用引用。
膜拜一下大爷的做法。
注意到这玩意儿是个子串,所以这个东西是可以hash的。比如说我们可以把区间中的每个数平方相加,然后与我们需要的等差数列的每个数的平方和check。(为什么不直接相加呢?因为那样很容易被小数据卡跪。。)为了跑得更快,显然我们需要自然溢出,但是这样做的话需要除6,所以我们把它们整体乘6即可。
代码(线段树+平衡树):
#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cassert>
const int N=3e5+5,M=3e5+5;
int a[N];
int n;
void in(int &x){
char c=getchar();
while(c<'0'||c>'9')c=getchar();
for(x=0;c>='0'&&c<='9';c=getchar())x=x*10+(c^'0');
}
struct TS{
int ch[2];
int key;
pair<int,int> data;
}treap[N];
int root;
int rand_32(){
return rand()<<17^rand()<<5^rand();
}
void out(TS node){
printf("{ch[0]=%d,ch[1]=%d,data=<%d,%d>}\n",node.ch[0],node.ch[1],node.data.first,node.data.second);
}
void outdfs(int node){
if(node){
printf("treap[%d]=",node);
out(treap[node]);
outdfs(treap[node].ch[0]);
outdfs(treap[node].ch[1]);
}
}
void rot(int node,int fa,bool dir){
treap[fa].ch[dir]=treap[node].ch[!dir];
treap[node].ch[!dir]=fa;
}
void add(int &node,int x){
if(node){
bool dir;
if(treap[node].data<treap[x].data){
add(treap[node].ch[1],x);
dir=1;
}
else{
add(treap[node].ch[0],x);
dir=0;
}
if(treap[node].key>treap[x].key){
rot(x,node,dir);
node=x;
}
}
else node=x;
}
void del(int &node,int x){
if(node==x)
if(treap[node].ch[0]||treap[node].ch[1]){
bool dir=treap[treap[node].ch[0]].key>treap[treap[node].ch[1]].key;
rot(node=treap[node].ch[dir],x,dir);
del(treap[node].ch[!dir],x);
}
else node=0;
else
if(treap[node].data<treap[x].data)del(treap[node].ch[1],x);
else del(treap[node].ch[0],x);
}
int pred(int x){
int ans=0;
for(int node=root;node;)
if(treap[node].data>=treap[x].data)node=treap[node].ch[0];
else{
if(treap[node].data.first==treap[x].data.first)ans=treap[node].data.second;
node=treap[node].ch[1];
}
return ans;
}
int succ(int x){
int ans=n+1;
for(int node=root;node;)
if(treap[node].data<=treap[x].data)node=treap[node].ch[1];
else{
if(treap[node].data.first==treap[x].data.first)ans=treap[node].data.second;
node=treap[node].ch[0];
}
return ans;
}
struct SS{
int min,max;
int gcd;
int minsucc;
}segt[N<<2];
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
#define lson node<<1,l,l+r>>1
#define rson node<<1|1,(l+r>>1)+1,r
void out(SS node){
printf("{min=%d,max=%d,gcd=%d,minsucc=%d}\n",node.min,node.max,node.gcd,node.minsucc);
}
SS pushup(SS ls,SS rs,int l,int mid,int r){
return (SS){min(ls.min,rs.min),max(ls.max,rs.max),gcd(abs(a[mid+1]-a[mid]),gcd(ls.gcd,rs.gcd)),min(ls.minsucc,rs.minsucc)};
}
void build(int node,int l,int r){
if(l==r){
treap[l]=(TS){0,0,rand_32(),make_pair(a[l],l)};
add(root,l);
segt[node]=(SS){a[l],a[l],0,succ(l)};
//outdfs(root);
//puts("");
}
else{
build(rson),build(lson);
segt[node]=pushup(segt[node<<1],segt[node<<1|1],l,l+r>>1,r);
}
//printf("[%d,%d]=",l,r);
//out(segt[node]);
}
void update(int node,int l,int r,int x,int y){
if(l==r){
del(root,x);
a[x]=y;
treap[x]=(TS){0,0,rand_32(),make_pair(y,x)};
add(root,x);
segt[node]=(SS){y,y,0,succ(x)};
return;
}
else{
if(x<=l+r>>1)update(lson,x,y);
else update(rson,x,y);
segt[node]=pushup(segt[node<<1],segt[node<<1|1],l,l+r>>1,r);
}
}
void pupdate(int node,int l,int r,int x){
if(l==r){
segt[node].minsucc=succ(x);
return;
}
else{
if(x<=l+r>>1)pupdate(lson,x);
else pupdate(rson,x);
segt[node]=pushup(segt[node<<1],segt[node<<1|1],l,l+r>>1,r);
}
}
SS query(int node,int l,int r,int L,int R){
//printf("query(%)")
if(l==L&&r==R)return segt[node];
if(R<=l+r>>1)return query(lson,L,R);
if(L>l+r>>1)return query(rson,L,R);
return pushup(query(lson,L,l+r>>1),query(rson,(l+r>>1)+1,R),L,l+r>>1,R);
}
int main(){
freopen("bzoj_4373.in","r",stdin);
//freopen("bzoj_4373.out","w",stdout);
treap[0].key=0x7fffffff;
int m;
in(n),in(m);
for(int i=1;i<=n;++i)in(a[i]);
build(1,1,n);
int op;
int x,y;
int l,r,k;
int yescnt=0;
SS ans;
int pre;
while(m--){
//printf("-----%d-----\n",m);
in(op);
if(op==1){
in(x),in(y);
x^=yescnt,y^=yescnt;
//printf("Update:%d=%d\n",x,y);
pre=pred(x);
update(1,1,n,x,y);
if(pre)pupdate(1,1,n,pre);
if(pre=pred(x))pupdate(1,1,n,pre);
}
else{
in(l),in(r),in(k);
l^=yescnt,r^=yescnt,k^=yescnt;
//printf("Query([%d,%d],%d)\n",l,r,k);
ans=query(1,1,n,l,r);
//printf("ans=");
//out(ans);
if(k?ans.gcd%k==0&&(ans.max-ans.min)/k==r-l&&ans.minsucc>r:ans.max==ans.min){
puts("Yes");
++yescnt;
}
else puts("No");
}
}
}
代码(hash):
#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=3e5+5,M=3e5+5;
char * cp=(char *)malloc(20000000);
void in(int &x){
while(*cp<'0'||*cp>'9')++cp;
for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}
const int Z=1<<19;
struct ZS{
int min;
int sum;
}zkw[Z<<1];
void pushup(int node){
zkw[node]=(ZS){min(zkw[node<<1].min,zkw[node<<1|1].min),zkw[node<<1].sum+zkw[node<<1|1].sum};
}
const int inf=0x7fffffff;
int main(){
freopen("bzoj_4373.in","r",stdin);
freopen("bzoj_4373_hash.out","w",stdout);
fread(cp,1,20000000,stdin);
int n,m;
in(n),in(m);
for(int i=1;i<=n;++i){
in(zkw[Z+i].min);
zkw[Z+i].sum=zkw[Z+i].min*zkw[Z+i].min*6;
}
for(int i=Z;--i;)pushup(i);
int op;
int x,y;
int l,r,len,k;
int yescnt=0;
ZS ans;
while(m--){
in(op);
if(op==1){
in(x),in(y);
x^=yescnt,y^=yescnt;
zkw[x+=Z]=(ZS){y,y*y*6};
while(x>>=1)pushup(x);
}
else{
in(l),in(r),in(k);
l^=yescnt,r^=yescnt,k^=yescnt;
len=r-l;
ans=(ZS){inf,0};
for(l+=Z-1,r+=Z+1;r-l>1;l>>=1,r>>=1){
if(~l&1){
//cout<<(l^1)-Z<<endl;
ans.min=min(ans.min,zkw[l^1].min);
ans.sum=ans.sum+zkw[l^1].sum;
//cout<<"Get\n";
}
if(r&1){
//cout<<(r^1)-Z<<endl;
ans.min=min(ans.min,zkw[r^1].min);
ans.sum=ans.sum+zkw[r^1].sum;
//cout<<"Get\n";
}
}
//cout<<ans.min<<" "<<ans.sum<<endl;
//cout<<(ans.min+ans.min+(r-l)*k)<<"*"<<(r-l+1)<<"->"<<((unsigned)((ans.min+ans.min+(r-l)*k)*(r-l+1))>>1)<<endl; //cout<<ans.min<<endl; //cout<<((unsigned)((ans.min+ans.min+len*k)*(len+1))>>1)<<endl; //cout<<(LL)ans.min*ans.min%Mod*(len+1)%Mod<<'+'<<(LL)len*(len+1)%Mod*ans.min%Mod*k%Mod<<'+'<<(LL)len*(len+1)%Mod*(len<<1|1)%Mod*pow(6,Mod-2)%Mod*k%Mod*k%Mod<<endl; if(ans.sum==ans.min*ans.min*(len+1)*6+len*(len+1)*ans.min*k*6+len*(len+1)*(len<<1|1)*k*k){ puts("Yes"); ++yescnt; } else puts("No"); } } }
总结:
①用引用写treap可以省去很多讨论。
②treap是可以不保存父指针的,这意味着一棵普通的treap也是可以可持久化的。(其实splay也是可以不保存父指针的,但是因为splay的时间复杂度是均摊的,所以它并不能可持久化。)
③判断两个子串是否相等?——hash!