1. 程式人生 > >Codeforces848C Goodbye Souvenir -- CDQ分治

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; }