您的位置:首页 > 运维架构

cf#204-div1 -D - Jeff and Removing Periods-莫队算法-维护等差数列

2016-05-14 11:55 423 查看
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
;
int aa
;

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
;     //bak[a[i]]:下一个值为a[i]的位置
int pre
;       //bak[a[i]]:上一个值为a[i]的位置
int last
;
int fl
,fr
;       // FL【i】表示i位置往左找,第一个使得a【i】不满足等差数列的位置,FR同理。
int cnt
;             //a[i]的个数
int dengcha=0;          //某数值全都形成等差数列排列的数的个数
int kind=0;             //不同数值的种类
int ans
;
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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: