Codeforces 848C Goodbye Souvenir

原题

Codeforces848C

题意

初始有一个长度为n的序列,进行两种操作
1. 修改一个位置上的数。
2. 查询l、r区间内每个值出现的长度之和。一个值在某个区间内出现的长度定义为这个值最后一次出现的位置第一次出现的位置的差。

解题思路

假设一个数x在区间内出现k次,出现的位置分别为p1、p2一直到pk,那么我们要计入答案的就是pk-p1
pk-p1=(pk-pk-1)+(pk-1-pk-2)+…+(p2-p1)
假设一个数当前在[l,r]区间内,并且它上次出现在位置也在[l,r]区间内,那么可以通过计算这个数当前位置和上次出现的位置的差来累加答案。
因此一个数累加入答案的条件就和它当前的位置和它上一次出现的位置有关,因此可以抽象成一个权值为x-pre[x]的点(pre[x],x),每次查询就相当于查询一个横坐标和纵坐标都在[l,r]范围内矩阵。
又由于pre[x] < x,因此满足条件的点就应该是l<=pre[x] < x<=r,查询就只需要查询横坐标>=l、纵坐标<=r的点的权值之和。
然后就可以用主席树了!然而似乎卡内存……
于是还是用CDQ分治吧……

每次读入修改操作后用set查找更新。

代码

#include <set>
#include <cstdio>

using namespace std;
typedef long long LL;

const int N=1e5+10;

struct Node 
{
    int x,y,op,val;
    Node(int x=0,int y=0,int op=0,int val=0):x(x),y(y),op(op),val(val){}
}C[N<<3],P[N<<3];

set<int>put[N];
int n,m,cnt,pos[N],c[N];
LL ans[N];

void Init()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
    {
        scanf("%d",&c[i]);
        put[c[i]].insert(i);
        if (pos[c[i]]) C[++cnt]=Node(i,pos[c[i]],0,i-pos[c[i]]);
        pos[c[i]]=i;
    }
}

void Change(int pos,int col)
{
    if (c[pos]==col) return ;
    //Erase in previous color set
    set<int>::iterator it=put[c[pos]].find(pos);
    int pre=0,last=0;
    it++;
    if (it!=put[c[pos]].end()) 
    {
        last=*it;
        C[++cnt]=Node(last,pos,0,pos-last);
    }
    it--;
    if (it!=put[c[pos]].begin()) 
    {
        it--;
        pre=*it;
        C[++cnt]=Node(pos,pre,0,pre-pos);
        it++;
    }
    if (last && pre) C[++cnt]=Node(last,pre,0,last-pre);
    put[c[pos]].erase(pos);

    //Insert in now color set
    pre=0; last=0;
    put[col].insert(pos);
    it=put[col].find(pos);
    it++;
    if (it!=put[col].end())
    {
        last=*it;
        C[++cnt]=Node(last,pos,0,last-pos);
    }
    it--;
    if (it!=put[col].begin())
    {
        it--;
        pre=*it;
        C[++cnt]=Node(pos,pre,0,pos-pre);
        it++;
    }
    if (last && pre) C[++cnt]=Node(last,pre,0,pre-last);

    //change color
    c[pos]=col;
}

struct Bit
{
    #define lowbit(x) (x&(-x))
    LL x[N];

    void Add(int set,int val)
    {
        for (;set<N;set+=lowbit(set))
            x[set]+=val;
    }

    LL Query(int set)
    {
        LL ret=0;
        for (;set;set-=lowbit(set))
            ret+=x[set];
        return ret;
    }

}bit;

void CDQ(int l,int r)
{
    if (l==r) return ;
    int mid=(l+r)>>1;
    CDQ(l,mid); CDQ(mid+1,r);

    for (int i=l;i<=r;++i) P[i]=C[i];
    int p1=l,p2=mid+1,top=l;
    while (p1<=mid || p2<=r)
    {
        if (p2>r || (p1<=mid && P[p1].x<=P[p2].x))
        {
            C[top++]=P[p1];
            if (!P[p1].op) bit.Add(n-P[p1].y+1,P[p1].val);
            p1++;
        }else 
        {
            C[top++]=P[p2];
            if (P[p2].op) ans[P[p2].val]+=bit.Query(n-P[p2].y+1);
            p2++;
        }
    }
    for (int i=l;i<p1;++i)
        if (!P[i].op) bit.Add(n-P[i].y+1,-P[i].val);

}

void Solve()
{
    int op,a,b,cntq=0;;
    for (int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&op,&a,&b);
        if (op==1) Change(a,b);
        else C[++cnt]=Node(b,a,1,++cntq);
    }
    CDQ(1,cnt);
    for (int i=1;i<=cntq;++i) printf("%I64d\n",ans[i]);
}

int main()
{
    Init();
    Solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值