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]]; }
如果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; }