http://codeforces.com/contest/351/problem/D
给你一个等差序列,每次查询一段区间【l,r】的答案。 显然这是典型的不带修改的区间询问类问题,我们可以考虑用莫队算法去解决。
接下来看怎么递推【l,r】到【l-1,r】,【l,r+1】的关系
首先对区间的询问是: 每次 任选ai,k,把ai删掉,如a【i+k】==a【i】则一直删下去,也就是把值相等,且下标形成等差数列的一列数删掉, 余下的数重新排列。
【问你删除完整个数列的最少操作数】
显然,第一次删掉后重排的话,我们肯定可以把所有数排成公差为1的一个个等差数列。
所以其实 询问的意思就是: 如果第一次能找到一个数值,其所有出现的数的下标都形成等差数列(可以一次性删除),那么之后的操作步数实际就是 数值的种类数。
因此 答案是 总种类数;
如果第一次找不到 一个数满足 所有出现的数的下标都形成等差数列 则答案就是 总的种类数+1
转移的话,需要预处理两个数组, FL【i】和FR【i】,FL【i】表示i位置往左找,第一个使得a【i】不满足等差数列的位置,FR同理。
根据以下递推式:
for (i=1; i<=m; i++) //预处理
{
pre[i]=last[aa[i]];//上一个a[i]出现的位置
last[aa[i]]=i;
if (pre[pre[i]]-pre[i]==pre[i]-i||pre[i]==0)//判断是否等差
fl[i]=fl[pre[i]];
else fl[i]=pre[pre[i]];
}
按照莫队算法,如果上一次答案区间为【l,r】,那么下一次要add一个数的话,
如果add的数第一次出现,则kind++, 且num_dengcha++
否则,看该数的加入是否会导致 num_dengcha减少。
即: if (fl[i]>=x&&fl[pre[i]]<x) dengcha--; // 之前是等差,现在不是等差,
如果是delete的话,
如果这个数只有一个,则显然kind--,num_dengcha--;
否则,看该数的减少会不会导致 num_dengcha的增加
即: if (fr[bak[i]]>R&&fr[i]<=R) dengcha++;
好了,接下来按照套路写莫队就好了
233ms:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const int N=100005;
const double pi=acos(-1.0);
double eps=0.000001;
struct node
{
int x,y,l,p;
};
node q[N];
int aa[N];
int L, R;
bool cmp(node a,node b)
{
if (a.l==b.l) return a.y<b.y;
return a.l<b.l;
}
int bak[N]; //bak[a[i]]:下一个值为a[i]的位置
int pre[N]; //bak[a[i]]:上一个值为a[i]的位置
int last[N];
int fl[N],fr[N]; // FL【i】表示i位置往左找,第一个使得a【i】不满足等差数列的位置,FR同理。
int cnt[N]; //a[i]的个数
int dengcha=0; //某数值全都形成等差数列排列的数的个数
int kind=0; //不同数值的种类
int ans[N];
void query(int x,int y,int flag)
{
if (flag)
{
for (int i=L-1; i>=x; i--)
{
if (cnt[aa[i]]==0)
{
dengcha++;
kind++;
}
else
{
if (fr[i]<=R&&fr[bak[i]]>R) dengcha--;//此时右端点还为R
}
cnt[aa[i]]++;
}
for (int i=L; i<x; i++)
{
if (cnt[aa[i]]==1)
{
dengcha--;
kind--;
}
else
{
if (fr[bak[i]]>R&&fr[i]<=R) dengcha++; //此时右端点还为R
}
cnt[aa[i]]--;
}
for (int i=R; i>=y+1; i--)
{
if (cnt[aa[i]]==1)
{
dengcha--;
kind--;
}
else
{
if (fl[pre[i]]<x&&fl[i]>=x) dengcha++;//此时左端点已经是x了
}
cnt[aa[i]]--;
}
for (int i=R+1; i<=y; i++)
{
if (cnt[aa[i]]==0)
{
dengcha++;
kind++;
}
else
{
if (fl[i]>=x&&fl[pre[i]]<x) dengcha--;
}
cnt[aa[i]]++;
}
}
else
{
for (int i=x; i<=y; i++)
{
if (cnt[aa[i]]==0)
{
dengcha++;
kind++;
}
else
{
if (fl[i]>=x&&fl[pre[i]]<x) dengcha--;
}
cnt[aa[i]]++;
}
}
L=x,R=y;
}
int main()
{
int m;
int i,j;
cin>>m;
for (i=1; i<=m; i++) //预处理
{
scanf("%d",&aa[i]);
pre[i]=last[aa[i]];
last[aa[i]]=i;
if (pre[pre[i]]-pre[i]==pre[i]-i||pre[i]==0)
fl[i]=fl[pre[i]];
else fl[i]=pre[pre[i]];
}
fill(last,m+last,m+1);
for (i=m; i>=1; i--)
{
bak[i]=last[aa[i]];
last[aa[i]]=i;
if (bak[bak[i]]-bak[i]==bak[i]-i||bak[i]==m+1)
fr[i]=fr[bak[i]];
else fr[i]=bak[bak[i]];
}
int qq;
cin>>qq;
int block_size=sqrt(1.0*m);
for (i=0; i<qq; i++)
{
scanf("%d%d",&q[i].x,&q[i].y);
q[i].l=q[i].x/block_size;
q[i].p=i;
}
sort(q,q+qq,cmp);
kind = 0;
for ( i=0; i<qq; i++)
{
query(q[i].x, q[i].y, i);
ans[q[i].p] = kind+1;
if (dengcha>0) ans[q[i].p]--;
}
for ( i=0; i<qq; i++) printf("%d\n", ans[i]);
return 0;
}