Codeforces848C Goodbye Souvenir -- CDQ分治

刚开始打了个树状数组套主席树,然后发现空间不够。。。

previ 表示 ai 上一个出现的位置。发现答案可以表示成

i=lr [lprevi]×(iprevi)

由于 previi ,还可以表示为
i=1r [lprevi]×(iprevi)

然后直接CDQ分治就可以了。

代码
#include<set>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100010
#define ll long long
set<int>s[N];
set<int>::iterator it;
struct Node{
    int p,x,w,f;
    Node(int p=0,int x=0,int w=0,int f=0):p(p),x(x),w(w),f(f){}
}b[N*10],t[N*10];
ll Ans[N],c[N],tt,v[N];
bool f[N];
int i,j,k,n,m,q,p,a[N];
int x,y;
int l[N],pr[N],nx[N];
inline void Modify(int x,int y){
    if(a[x]==y)return;
    it=s[a[x]].find(x);
    int p=0;
    if(it!=s[a[x]].begin()){
        it--;p=(*it);it++;
        b[++m]=Node(x,p,p-x);
    }
    it++;
    if(it!=s[a[x]].end()){
        b[++m]=Node(*it,x,x-(*it));
        if(p)b[++m]=Node(*it,p,(*it)-p);
    }
    s[a[x]].erase(x);a[x]=y;
    it=s[y].insert(x).first;
    p=0;
    if(it!=s[y].begin()){
        it--;p=(*it);it++;
        b[++m]=Node(x,p,x-p);
    }
    it++;
    if(it!=s[y].end()){
        if(p)b[++m]=Node(*it,p,p-(*it));
        b[++m]=Node(*it,x,(*it)-x);
    }
}
inline void Update(int x,int y){
    for(;x<=n;x+=x&-x)
    if(v[x]!=tt)v[x]=tt,c[x]=y;else c[x]+=y;
}
inline ll Query(int x){
    ll Ans=0;
    for(;x;x-=x&-x)
    if(v[x]!=tt)v[x]=tt,c[x]=0;else Ans+=c[x];
    return Ans;
}
inline void Solve(int l,int r){
    if(l==r)return;
    int Mid=l+r>>1;
    Solve(l,Mid);Solve(Mid+1,r);
    int p1=l,p2=Mid+1;
    ++tt;
    for(int i=l;i<=r;i++)
    if(p2>r||(p1<=Mid&&b[p1].p<=b[p2].p)){
        t[i]=b[p1++];
        if(!t[i].f)Update(n-t[i].x+1,t[i].w);
    }else{
        t[i]=b[p2++];
        if(t[i].f)Ans[t[i].f]+=Query(n-t[i].x+1);
    }
    for(int i=l;i<=r;i++)b[i]=t[i];
}
int main(){
    scanf("%d%d",&n,&q);
    for(i=1;i<=n;i++)scanf("%d",&a[i]),s[a[i]].insert(i);
    for(i=1;i<=n;i++){
        it=s[a[i]].find(i);
        if(it!=s[a[i]].begin()){
            it--;
            b[++m]=Node(i,*it,i-(*it));
        }
    }
    for(i=1;i<=q;i++){
        scanf("%d%d%d",&k,&x,&y);
        if(k==1)Modify(x,y);else f[i]=1,b[++m]=Node(y,x,0,i);
    }
    Solve(1,m);
    for(i=1;i<=q;i++)if(f[i])printf("%I64d\n",Ans[i]);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值