Uva12345: Dynamic len(set(a[L:R]))
可修改莫队 OR 线段树套树状数组 OR 分块
题解:
解法1:
可修改莫队。每块 n23 ,总复杂度 O(n53) .
Code:
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #define D(x) cout<<#x<<" = "<<x<<" " #define E cout<<endl using namespace std; const int mxn = 1e6+5; const int N = 5e4+5; int n,m,csz,qsz,bsz,cnt[mxn],ans[N],res,lpos,rpos,now,a[N],tp[N]; struct Que{ int l,r,tim,id; } q[N]; bool cmp(const Que &a,const Que &b){ if(a.l/bsz != b.l/bsz) return a.l/bsz < b.l/bsz; else return a.r<b.r || (a.r==b.r && a.tim<b.tim); } struct Change{ int p,x,y,tim; } c[N]; void ins(int p){ cnt[a[p]]++; if(cnt[a[p]]==1) res++; } void erase(int p){ cnt[a[p]]--; if(cnt[a[p]]==0) res--; } void change(int i,int op){ // printf("change: %d %d\n",i,op); if(lpos<=c[i].p && c[i].p<=rpos) erase(c[i].p); a[c[i].p]=op?c[i].y:c[i].x; if(lpos<=c[i].p && c[i].p<=rpos) ins(c[i].p); } int main(){ freopen("a.in","r",stdin); char op[5]; int x,y; scanf("%d%d",&n,&m); for(bsz=1;bsz*bsz*bsz<=n;bsz++); bsz=bsz*bsz; for(int i=1;i<=n;i++) scanf("%d",a+i), tp[i]=a[i]; for(int i=1;i<=m;i++){ scanf("%s%d%d",op,&x,&y); if(op[0]=='M'){ x++; c[++csz].p=x; c[csz].x=tp[x]; c[csz].y=y; c[csz].tim=i; tp[x]=y; } else{ x++; q[++qsz].l=x; q[qsz].r=y; q[qsz].tim=i; q[qsz].id=qsz; } } sort(q+1,q+1+qsz,cmp); lpos=1, rpos=0, now=0; for(int i=1;i<=qsz;i++){ // D(q[i].l); D(q[i].r); D(q[i].tim); E; while(lpos<q[i].l){ erase(lpos); lpos++; } while(lpos>q[i].l){ lpos--; ins(lpos); } while(rpos<q[i].r){ rpos++; ins(rpos); } while(rpos>q[i].r){ erase(rpos); rpos--; } while(now<csz && c[now+1].tim<q[i].tim){ now++; change(now,1); } while(now>0 && c[now].tim>q[i].tim){ change(now,0); now--; } ans[q[i].id]=res; // D(lpos); D(rpos); D(now); E; // for(int i=1;i<=n;i++)cout<<a[i]<<" "; cout<<endl; // for(int i=1;i<=5;i++)cout<<cnt[i]<<" "; cout<<endl; } for(int i=1;i<=qsz;i++) printf("%d\n",ans[i]); }
解法2:
设pre[i]表示i前面一个和a[i]相同的字符的位置。
答案就是长度减去pre[i]大于等于l的个数。i∈[l,r].
就用权值BIT维护pre,每个bit节点上套一个位置的权值线段树即可。
线段树动态开点,复杂度 O(mlog2n) .
Code:
#include <iostream> #include <cstring> #include <cstdio> #include <set> #define D(x) cout<<#x<<" = "<<x<<" " #define E cout<<endl using namespace std; const int mxn = 1000000; const int N = 50005; int n,m,a[N],pre[N],lst[N]; int lch[N*20],rch[N*20],cnt[N*20],sz; set<int> s[mxn+5]; void change(int &x,int p,int d,int l=1,int r=n){ if(!x) x=++sz; cnt[x]+=d; // printf("change: "); D(x); D(p); D(d); D(l); D(r); E; if(l==r) return; int mid=(l+r)>>1; if(p<=mid) change(lch[x],p,d,l,mid); else change(rch[x],p,d,mid+1,r); } int query(int x,int ql,int qr,int l=1,int r=n){ if(!x) return 0; // printf("query: "); D(x); D(ql); D(qr); D(l); D(r); E; if(ql<=l && r<=qr){ // D(cnt[x]); E; return cnt[x]; } else{ int mid=(l+r)>>1, ans=0; if(ql<=mid) ans+=query(lch[x],ql,qr,l,mid); if(qr>mid) ans+=query(rch[x],ql,qr,mid+1,r); return ans; } } struct BIT{ void add(int x,int p,int d){ x++; while(x<=n){ change(x,p,d); x+=x&(-x); } } int sum(int x,int ql,int qr){ int ans=0; x++; while(x>0){ ans+=query(x,ql,qr); x-=x&(-x); } return ans; } } bit; int Nxt(int x){ // D(x); D(a[x]); E; set<int>::iterator it=s[a[x]].find(x); if(++it!=s[a[x]].end()) return *it; else return -1; } int Pre(int x){ set<int>::iterator it=s[a[x]].find(x); if(it!=s[a[x]].begin()) return*(--it); else return 0; } int main(){ freopen("a.in","r",stdin); freopen("b.out","w",stdout); int x,y,nx,pr,ans; char op[5]; scanf("%d%d",&n,&m); sz=n; for(int i=1;i<=n;i++) scanf("%d",a+i); for(int i=1;i<=n;i++){ pre[i]=lst[a[i]]; lst[a[i]]=i; // D(pre[i]); E; } for(int i=1;i<=n;i++){ // D(i); E; bit.add(pre[i],i,1); s[a[i]].insert(i); } // for(int i=1;i<=n;i++){ // cout<<query(pre[i]+1,i,i)<<endl; // } for(int i=1;i<=m;i++){ scanf("%s%d%d",op,&x,&y); x++; if(op[0]=='M'){ nx=Nxt(x); //D(nx); E; if(nx!=-1){ bit.add(pre[nx],nx,-1); bit.add(pre[x],nx,1); pre[nx]=pre[x]; } s[a[x]].erase(x); a[x]=y; s[a[x]].insert(x); nx=Nxt(x); //D(nx); E; if(nx!=-1){ bit.add(pre[nx],nx,-1); bit.add(x,nx,1); pre[nx]=x; } pr=Pre(x); //D(pr); E; bit.add(pre[x],x,-1); bit.add(pr,x,1); pre[x]=pr; } else{ ans=bit.sum(x-1,x,y); printf("%d\n",ans); } } }
解法3:
还是使用解法2的pre思想,分块,把每块中的pre排好序。
询问:零散的直接查,整块的二分查找。
修改:修改相应的值并不停交换相邻两个直到重新排好序。
复杂度: O(nn√logn√) .
Code:
//目测比较简单