带修改的主席树
对于单点修改的主席树,我们可以采用树套树来写,原因 不会整体二分求带修改的区间第k小。
所以学习了一波 待修改的主席树。真的是难写,或者说码量有点大。
不过和三维偏序CDQ分治相比其实差不了多少,但是CDQ终究比树套树快而且空间消耗小。
两者都很不错!
经典题,但是对于我这个根本不懂树套树的人来说是有点难度的考虑整体二分吧,但是呢不会。
所以上主席树,核心:主席树维护区间 树状数组维护位置。
还不懂么?先想如果直接线段树的话 每次修改后面的主席树都要改一遍然后 复杂度 n^2logn
如何优化 考虑树状数组吧 每次修改我们至多修改 logn棵主席树 每棵主席树的修改是 logn的。
复杂度 log^2n 所以我们愉快的以总时间复杂度 nlog^2n 空间复杂度 (n+m)logn
都还行。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<cctype> #include<utility> #include<queue> #include<stack> #include<deque> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<ctime> #include<set> #include<bitset> #include<map> #include<cmath> #include<vector> #include<cstdlib> #define INF 2147483646 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar(10);return; } const int MAXN=100002; struct wy { int l,r; int v; }t[60000000]; struct wy1 { int l,r,k; }g[MAXN<<1]; int n,m; int a[MAXN<<1],b[MAXN<<1],cnt,num; int root[MAXN<<1],cnt1,ans; int q[MAXN],q1[MAXN],top,top1; char ch[5]; void discrete() { sort(b+1,b+1+cnt); for(int i=1;i<=cnt;i++)if(i==1||b[i]!=b[i-1])b[++num]=b[i]; for(int i=1;i<=cnt;i++)a[i]=lower_bound(b+1,b+1+num,a[i])-b; } void change(int &now,int l,int r,int w,int x,int d) { now=++cnt1;t[now]=t[w]; t[now].v+=d; if(l==r)return; int mid=(l+r)>>1; if(x<=mid)change(t[now].l,l,mid,t[w].l,x,d); else change(t[now].r,mid+1,r,t[w].r,x,d); } void add(int x,int y,int v) { for(;x<=n;x+=x&(-x))change(root[x],1,num,root[x],v,y); } int sigma() { int s=0,s1=0; for(int i=1;i<=top1;i++)s1+=t[t[q1[i]].l].v; for(int i=1;i<=top;i++)s+=t[t[q[i]].l].v; return s-s1; } void ask(int x,int y,int l,int r,int k) { top=top1=0; for(;x;x-=x&(-x))q1[++top1]=root[x]; for(;y;y-=y&(-y))q[++top]=root[y]; while(l<r) { int mid=(l+r)>>1; int sum=sigma(); if(sum>=k) { for(int i=1;i<=top1;i++)q1[i]=t[q1[i]].l; for(int i=1;i<=top;i++)q[i]=t[q[i]].l; r=mid; } else { for(int i=1;i<=top1;i++)q1[i]=t[q1[i]].r; for(int i=1;i<=top;i++)q[i]=t[q[i]].r; k-=sum;l=mid+1; } } ans=l;return; } int main() { //freopen("1.in","r",stdin); n=read();m=read();cnt=n; for(int i=1;i<=n;i++)a[i]=b[i]=read(); for(int i=1;i<=m;i++) { scanf("%s",&ch); ++cnt; if(ch[0]=='Q') { g[i].l=read(); g[i].r=read(); g[i].k=read(); b[cnt]=INF; a[cnt]=INF; } else { g[i].l=read(); g[i].r=read(); a[cnt]=g[i].r; b[cnt]=g[i].r; } } discrete(); //for(int i=1;i<=n;i++)cout<<a[i]<<' ';puts(""); //for(int i=1;i<=n;i++)cout<<b[a[i]]<<' ';puts(""); for(int i=1;i<=n;i++)add(i,1,a[i]); for(int i=1;i<=m;i++) { if(g[i].k) { ask(g[i].l-1,g[i].r,1,num,g[i].k); put(b[ans]); } else { add(g[i].l,-1,a[g[i].l]); //cout<<g[i].l<<endl; //cout<<a[g[i].l]<<' '<<b[a[g[i].l]]<<endl; add(g[i].l,1,a[i+n]); a[g[i].l]=a[i+n]; //cout<<a[g[i].l]<<' '<<b[a[g[i].l]]<<endl; } } return 0; }
我是觉得有点难写了,但是仍然是坚持码完了。
这道题呢其实也是可以树套树的,考虑先将整体逆序对全部求出然后 每次动态的删除某个数(log^2n)
然后计算删掉这个数后对答案的总影响即可。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<cctype> #include<utility> #include<queue> #include<stack> #include<deque> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<ctime> #include<set> #include<bitset> #include<map> #include<cmath> #include<vector> #include<cstdlib> #define INF 2147483646 #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void put(ll x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=100002; int n,m,cnt; ll ans; int a[MAXN],c[MAXN]; int root[MAXN<<1],pos[MAXN]; int q[50],q1[50],top,top1; struct wy { int l,r,v; }t[28900000];//空间 nlog^2n 3e7左右 貌似300多MB但是应该能过 void add(int x,int y){for(;x<=n;x+=x&(-x))c[x]+=y;} int ask(int x) { int cnt=0; for(;x;x-=x&(-x))cnt+=c[x]; return cnt; } void state() { for(int i=n;i>=1;i--) { ans+=ask(a[i]-1); add(a[i],1); } return; } void change(int &now,int l,int r,int now1,int x,int y) { if(now==0)now=++cnt; t[now]=t[now1]; t[now].v+=y; if(l==r)return; int mid=(l+r)>>1; if(x<=mid)change(t[now].l,l,mid,t[now1].l,x,y); else change(t[now].r,mid+1,r,t[now1].r,x,y); } void insert(int x,int y) { for(int i=x;i<=n;i+=i&(-i)) change(root[i],1,n,root[i],a[x],y); } int ask(int x,int y,int d,int p) { top=top1=0; for(int i=x-1;i;i-=i&(-i))q[++top]=root[i]; for(int i=y;i;i-=i&(-i))q1[++top1]=root[i]; int l=1,r=n,num=0,num1=0,num2=0; while(l!=r) { int mid=(l+r)>>1; if(d>mid) { if(p==0) //询问当前有多少数字比d小 { num1=0,num2=0; for(int i=1;i<=top;i++)num2+=t[t[q[i]].l].v; for(int i=1;i<=top1;i++)num1+=t[t[q1[i]].l].v; num+=num1-num2; } for(int i=1;i<=top;i++)q[i]=t[q[i]].r; for(int i=1;i<=top1;i++)q1[i]=t[q1[i]].r; l=mid+1; } else { if(p==1) { num1=0,num2=0; for(int i=1;i<=top;i++)num2+=t[t[q[i]].r].v; for(int i=1;i<=top1;i++)num1+=t[t[q1[i]].r].v; num+=num1-num2; } for(int i=1;i<=top;i++)q[i]=t[q[i]].l; for(int i=1;i<=top1;i++)q1[i]=t[q1[i]].l; r=mid; } } return num; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;i++)a[i]=read(),pos[a[i]]=i; state(); for(int i=1;i<=n;i++)insert(i,1); for(int i=1;i<=m;i++) { put(ans); int x=read(); insert(pos[x],-1); ans-=ask(1,pos[x]-1,x,1); ans-=ask(pos[x]+1,n,x,0); } return 0; }
我也不知道还能坚持多久。