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

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;

}




你可能感兴趣的:(cf#204-div1 -D - Jeff and Removing Periods-莫队算法-维护等差数列)