CF351D Jeff and Removing Periods
CF351D Jeff and Removing Periods
首先需要想到的是一个结论:
对于一个区间,如果其存在某一个值的位置集合构成等差数列,那么这个区间的答案就是区间颜色个数,如果不存在(也就是说所有值的位置集合都不能构成等差数列),那么这个区间的答案就是区间颜色个数+1,原因显然。
正确性显然,因为我们相当于把第一次删除的机会拿来对区间所有元素“排序”了,排序过后,答案显然就是区间颜色个数了,因为都构成了差为 1 的等差数列。
而如果我们第一次可以不浪费地再删掉一个元素,那么我们何乐而不为呢?于是就有了这个加一和不加一的区别。
而这个问题当中,区间数颜色是一个经典问题,不再赘述,具体可以看P1972 [SDOI2009]HH的项链,这里我们使用莫队来维护。
那么我们现在的问题就是:对于所有值,维护其位置集合当前是否是等差数列。
我们可以考虑这样来做:对于每一个值维护一个双端队列,然后每次插入和删除相当于都是在队首和队尾操作。
然后思考怎么快速判断当前的双端队列是否是一个等差数列。
我们发现可以对每一个值维护一个并查集,也就是说,我们可以在原序列中维护每一个点可以形成的等差数列的最左端,也就是说,我们每次到一个值,就询问这个值的上一个位置以及上一个位置结尾的等差数列对应的公差。
然后我们检查一下当前点可不可以接上去,可以就把当前点父亲设为上一个位置,不可以就是其本身,再更新公差和位置即可。
判断的话就是取出当前的第 2 项,然后在并查集里查看和最后一项是否在同一个集合中即可。
可能这里大家会有一个问题:为什么中间的位置就不用判断了呢?
这是因为我们这里的并查集一定是“连续”的,也就是说,因为每一个点要么和自己连,要么可以去最接近的上一个点,所以如果右端点和左端点在同一个集合,那么右端点肯定是通过中间一一相连在到了左端点,于是这里相当于自动判断了中间点。
最后,时间复杂度是 \(O(n\times\alpha(n)\times\sqrt{n})\)。(这里偷懒没有写按秩合并就当作是 \(O(nlogn\sqrt{n})\) 吧)
代码:
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
const int N=1e5+5;
#define ll long long
int n,m,k;
int bl[N],fa[N];
int a[N],Ans[N],cnt[N],pos[N],d[N];
struct Query{int l,r,id;}Q[N];
int Now,Num;
deque<int> q[N];
int Getfa(int x){return x==fa[x]?x:fa[x]=Getfa(fa[x]);}
inline bool Check(int val){
if(q[val].empty()) return false;
int l=q[val].front(),r=q[val].back();
if(l==r) return true;
q[val].pop_front();
int g=q[val].front();
bool ret=(Getfa(g)==Getfa(r));
q[val].push_front(l);
return ret;
}
inline void Add(int x,bool f){
if(Check(a[x])) Num--;
if(f==false) q[a[x]].push_front(x);
else q[a[x]].push_back(x);
cnt[a[x]]++;
if(cnt[a[x]]==1) Now++;
if(Check(a[x])) Num++;
return ;
}
inline void Del(int x,bool f){
if(Check(a[x])) Num--;
if(f==false) q[a[x]].pop_front();
else q[a[x]].pop_back();
cnt[a[x]]--;
if(cnt[a[x]]==0) Now--;
if(Check(a[x])) Num++;
return ;
}
inline bool Cmp(Query x,Query y){return bl[x.l]^bl[y.l]?bl[x.l]<bl[y.l]:bl[x.l]&1?x.r<y.r:x.r>y.r;}
int main(){
read(n);
for(int i=1;i<=n;i++){
read(a[i]);
if(!pos[a[i]]) fa[i]=-1,d[i]=0;
else{
if(i-pos[a[i]]==d[pos[a[i]]]) fa[i]=pos[a[i]];
else fa[i]=i;
d[i]=i-pos[a[i]];
}
pos[a[i]]=i;
}
read(m);
const int t=sqrt(m);
for(int i=1;i<=m;i++) read(Q[i].l),read(Q[i].r),Q[i].id=i;
for(int i=1;i<=n;i++) bl[i]=(i-1)/t+1;
sort(Q+1,Q+m+1,Cmp);
int l=1,r=0;
for(int i=1;i<=m;i++){
while(l>Q[i].l) Add(--l,false);
while(r<Q[i].r) Add(++r,true);
while(l<Q[i].l) Del(l++,false);
while(r>Q[i].r) Del(r--,true);
Ans[Q[i].id]=Now+(Num==0?1:0);
}
for(int i=1;i<=m;i++) write(Ans[i]),putchar('\n');
return 0;
}