整体二分
随便写一点整体二分的东西。
这个整体二分啊,非常的简单
拿最简单的出来说吧
poj2104
n,m<=100000
给一个长为n的数列a,有m个询问
每次输入l,r,k询问al~ar中第k小的是哪一个。
【solution】
你们可能说主席树。
然而有一个空间只要O(n)的做法,没错,就是整体二分。
那么这个整体二分是什么呢。
首先,我们把询问丢进一个struct 里面
然后我们二分一个答案mid
然后我们O(nlogn)求出每个询问的范围中<=mid的数的个数tot
显然啊,如果这个数量tot>k的话,显然这个询问的答案就<mid
那么我们现在就把所有的询问分成的两半。
分治递归下去做。
完了。时间O(nlogn^2)
是不是很简单啊。
以后出给学妹做,然后学妹写主席树空间被卡,
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈。
但是注意一点哦,这个过程中询问的顺序一定要保证哦。
否则的话,就乱套了。
poj2104这个代码比较gay,去看后面那个比较好。
#include<stdio.h> #include<stdlib.h> #include<iostream> #include<string> #include<string.h> #include<algorithm> #include<math.h> #define il inline #define re register #define lim 1e9 #define lowbit(x) (x&(-x)) using namespace std; const int N=1000001; struct data{int u,v; } a[N]; struct query{int l,r,k,id,tot; } qu[N],qt[N]; int n,m,c[N],ans[N]; il int cmp(data a,data b){ return a.v<b.v; } il void add(int p,int v){ for(;p<=n;p+=lowbit(p)) c[p]+=v; } il int sum(int p){ int ans=0; for(;p;p-=lowbit(p)) ans+=c[p]; return ans; } il void conquer(int l,int r,int p,int q){ int L=1,R=n+1,MID; while(L<R){ MID=(L+R)/2; if(a[MID].v>=p) R=MID; else L=MID+1; } for(int i=R;i<=n&&a[i].v<=q;i++) add(a[i].u,1); for(int i=l;i<=r;i++) qu[i].tot=sum(qu[i].r)-sum(qu[i].l-1); for(int i=R;i<=n&&a[i].v<=q;i++) add(a[i].u,-1); } il void divide(int l,int r,int p,int q){ // cout<<l<<" "<<r<<" "<<p<<" "<<q<<endl; if(p==q){ for(int i=l;i<=r;i++) ans[qu[i].id]=q; return; } int mid=p+(q-p)/2; conquer(l,r,p,mid); int L=l-1,R=r+1; for(int i=l;i<=r;i++){ if(qu[i].tot>=qu[i].k) qt[++L]=qu[i]; else{ qu[i].k-=qu[i].tot; qt[--R]=qu[i]; } } for(int i=l;i<=r;i++) qu[i]=qt[i]; if(L>=l) divide(l,L,p,mid); if(R<=r) divide(R,r,mid+1,q); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ a[i].u=i; scanf("%d",&a[i].v); } sort(a+1,a+n+1,cmp);a[n+1].v=lim; for(int i=1;i<=m;i++){ scanf("%d%d%d",&qu[i].l,&qu[i].r,&qu[i].k); qu[i].id=i; } divide(1,m,-lim,lim); for(int i=1;i<=m;i++){ printf("%d\n",ans[i]); } return 0; }
hdu5412
就是前面一题加一个修改,
这个也不虚,修改就是删掉一个数再加回来。
和询问一样丢进去分治,直接按照数的大小拿去二分。
重要的事情再讲一遍,注意顺序哦。
自认为这个代码写的比较清楚。
#include<stdio.h> #include<stdlib.h> #include<iostream> #include<string> #include<string.h> #include<algorithm> #include<math.h> #include<queue> #include<map> #include<vector> #include<set> #define il inline #define re register #define lim 1000000000 #define lowbit(p) (p&(-p)) using namespace std; const int N=1000001; struct edge{int u,v,w,id,tot; } a[N],d[N],e[N]; int n,m,b[N],M,t[N],c[N],ans[N]; il void add(int p,int v){ for(;p<=n;p+=lowbit(p)) c[p]+=v; } il int sum(int p){ int ans=0; for(;p;p-=lowbit(p)) ans+=c[p]; return ans; } il void divide(int l,int r,int p,int q){ if(p>q||l>r) return; if(p==q){ for(int i=l;i<=r;i++) if(a[i].w) ans[a[i].id]=p; return; } int mid=(p+q)/2; for(int i=l;i<=r;i++){ if(a[i].w) t[i]=sum(a[i].v)-sum(a[i].u-1); else if(a[i].u<=mid) add(a[i].id,a[i].v); } for(int i=l;i<=r;i++){ if(a[i].u<=mid&&a[i].w==0) add(a[i].id,-a[i].v); } int t1,t2,t3=l-1;t1=t2=0; for(int i=l;i<=r;i++){ if(a[i].w){ if(a[i].tot+t[i]>=a[i].w) d[++t1]=a[i]; else{ a[i].tot+=t[i];e[++t2]=a[i]; } } else if(a[i].u<=mid) d[++t1]=a[i]; else e[++t2]=a[i]; } for(int i=1;i<=t1;i++) a[++t3]=d[i]; for(int i=1;i<=t2;i++) a[++t3]=e[i]; divide(l,l+t1-1,p,mid);divide(l+t1,r,mid+1,q); } il void init(){ memset(ans,false,sizeof(ans)); memset(c,false,sizeof(c)); memset(t,false,sizeof(t)); memset(b,false,sizeof(b)); M=0; for(int i=1,x;i<=n;i++){ scanf("%d",&x);b[i]=x; a[++M]=(edge){x,1,0,i,0}; } scanf("%d",&m); for(int i=1,s,t,k,tp;i<=m;i++){ scanf("%d%d%d",&tp,&s,&t); if(tp==1){ a[++M]=(edge){b[s],-1,0,s,0}; b[s]=t; a[++M]=(edge){b[s],1,0,s,0}; } if(tp==2){ scanf("%d",&k); a[++M]=(edge){s,t,k,i,0}; } } divide(1,M,1,lim); for(int i=1;i<=m;i++) if(ans[i]) printf("%d\n",ans[i]); } int main(){ while(scanf("%d",&n)!=EOF) init(); return 0; }
ZJOI2013&bzoj3110
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint
【solution】
想了一会发现傻逼了。
这不就是又改了一点嘛,感觉大概彻底理解这个算法了。
网上不是树套树就是暴力瞎艹,
这时候写一个整体二分就不错!
就是添加的时候变成区修区查的树状数组就好了。
#include<stdio.h> #include<stdlib.h> #include<iostream> #include<string> #include<string.h> #include<algorithm> #include<math.h> #include<queue> #include<map> #include<vector> #include<set> #define il inline #define re register #define lowbit(p) (p&(-p)) using namespace std; typedef long long ll; const int N=1000001; struct edge{ int u,v,w,id,tp,tot; } a[N],a1[N],a2[N]; int n,m,M,ans[N]; ll t[N],c[N][2]; il void add(re int p,re ll v,re int k){ for(;p<=n;p+=lowbit(p)) c[p][k]+=v; } il ll sum(int p,int k){ ll ans=0; for(;p;p-=lowbit(p)) ans+=c[p][k]; return ans; } il void addr(int l,int r,int v){ add(l,v,0);add(r+1,-v,0); add(l,-(ll)v*(l-1),1);add(r+1,(ll)v*r,1); } il ll sumr(int r){ return sum(r,0)*r+sum(r,1); } il ll val(int l,int r){ return sumr(r)-sumr(l-1); } il void divide(int l,int r,int p,int q){ if(l>r||p>q) return; if(p==q){ for(int i=l;i<=r;i++) if(a[i].tp==2) ans[a[i].id]=p; return; } int mid=p+(q-p)/2; for(int i=l;i<=r;i++){ if(a[i].tp==2) t[i]=val(a[i].u,a[i].v); else if(a[i].w<=mid) addr(a[i].u,a[i].v,1); } for(int i=l;i<=r;i++) if(a[i].tp==1&&a[i].w<=mid) addr(a[i].u,a[i].v,-1); int t1,t2,t3=l-1;t1=t2=0; for(int i=l;i<=r;i++){ if(a[i].tp==1){ if(a[i].w<=mid) a1[++t1]=a[i]; else a2[++t2]=a[i]; } else{ if(a[i].tot+t[i]>=a[i].w) a1[++t1]=a[i]; else{ a[i].tot+=t[i]; a2[++t2]=a[i]; } } } for(int i=1;i<=t1;i++) a[++t3]=a1[i]; for(int i=1;i<=t2;i++) a[++t3]=a2[i]; divide(l,l+t1-1,p,mid); divide(l+t1,r,mid+1,q); } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y,z,rp;i<=m;i++){ scanf("%d%d%d%d",&rp,&x,&y,&z); if(rp==1) z=-z; a[++M]=(edge){x,y,z,i,rp,0};ans[i]=-1e9; } divide(1,m,-n,n); for(int i=1;i<=m;i++) if(ans[i]!=-1e9) printf("%d\n",-ans[i]); return 0; }