整体二分一般解决:边修改边询问,询问一般询问区间[l,r]里第k小/大
思想:整体二分相当于对所有询问与修改一起二分,二分的是那个第k大/小
解决办法:solve(l,r,L,R),表示处理位于(l,r)(这里的l,r,并不是原询问顺序,而是将询问排序到这一层时的l,r具体理解看下面)中的询问,L,R是二分的范围。
首先,当L=R时,将l,r内的所有答案赋为L
当L!=R;
int md=L+(R-L)/2
对于会影响到md+/-的修改,将他的影响“体现”出来(比如二分了md,问的是第k小,如果增加一个小于md的值,那么就在树状数组(只是一种表现形式)里这个位置+1)然后将这个修改放在左/右半边,否则放在右/左半边。
对于已经满足条件的询问(比如这个区间里的值大于等于这个询问里的k),将它归到左/右半边去,否则k减去当前区间里的值(因为这些值是由满足<=md的询问产生的,它们都会被归到左/右边去,在下一层的右/左半边都不会再被算,但其实对这个询问有影响,所以要把这些值算进去),然后归到右/左半边。
重新排序,按照原来时序排左边,再按原来时序排右边,将两者合并,得到新的序列
将“体现”出来的影响消去。
最后递归solve(l,l+左半边个数-1,L,md),solve(l+左半边个数,r,md+1,R)。
下帖例题
bzoj2527: [Poi2011]Meteors
这个题要注意是一个环,所以当跨n时要特殊处理
体现影响的容器选用树状数组即可,每次插入使用差分的思想,恰好树状数组是求前缀和
这里的询问不具有时序,而流星雨场数恰好就是我们要求得,所以我们可以只二分询问,对于每个md直接暴力的把小于md的流星雨加进来,大于的减去即可。
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N=300050;
int n,m;
long long c[N];
int cn=0;
int head[N],to[N],next[N];
void add1(int f,int t)
{
to[++cn]=t;
next[cn]=head[f];
head[f]=cn;
}
int lowbit(int x)
{
return x&(-x);
}
void plu(int pos,long long x)
{
while(pos<=m)
c[pos]+=x,pos+=lowbit(pos);
}
long long sum(int end)
{
long long sum1=0;
while(end>0)
sum1+=c[end],end-=lowbit(end);
return sum1;
}
struct caozuo
{
int l,r;
long long x;
}T[N];
void add(caozuo a,int xx)
{
if(a.l<=a.r)
plu(a.l,a.x*xx),plu(a.r+1,(0LL-a.x)*xx);
else
plu(a.l,a.x*xx),plu(1,a.x*xx),plu(a.r+1,(0LL-a.x)*xx);
}
int cnt,t,ans[N],id[N];
int a[N],o[N],tmp[N],mark[N];
void solve(int l,int r,int L,int R)
{
if(l>r)
return ;
if(L==R)
{
for(int i=l;i<=r;i++)
ans[id[i]]=L;
return;
}
int mid=(L+R)/2;
while(cnt<=mid) add(T[++cnt],1);
while(cnt>mid) add(T[cnt--],-1);
int cnt1=0;
for(int i=l;i<=r;i++)
{
long long tot=0;
int now=id[i];
for(int j=head[now];j!=-1;j=next[j])
{
tot+=sum(to[j]);
if(tot>=a[now])
break;
}
if(tot>=a[now])
mark[now]=1,cnt1++;
else
mark[now]=0;
}
int l1=l,l2=l+cnt1;
for(int i=l;i<=r;i++)
{
if(mark[id[i]]) tmp[l1++]=id[i];
else tmp[l2++]=id[i];
}
for(int i=l;i<=r;i++)
id[i]=tmp[i];
solve(l,l1-1,L,mid);
solve(l1,l2-1,mid+1,R);
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d",&o[i]),add1(o[i],i);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),id[i]=i;
scanf("%d",&t);
for(int i=1;i<=t;i++)
scanf("%d%d%lld",&T[i].l,&T[i].r,&T[i].x);
T[++t].l=1,T[t].r=m,T[t].x=1000000000LL;
solve(1,n,1,t);
for(int i=1;i<=n;i++)
{
if(ans[i]==t)
printf("NIE\n");
else
printf("%d\n",ans[i]);
}
}
bzoj3110: [Zjoi2013]K大数查询
在一段里每个位置加上一个数,每次问的是一个区间,所以影响体现容器就用一棵线段树即可。
这里修改与询问都具有时序,所以就老老实实的按开头说的那样写就好了
#include<iostream>
#include<math.h>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#define lson rt*2
#define rson rt*2+1
#define mid (l+r)/2
using namespace std;
const int N=500050;
typedef long long ll;
vector<int> lp,rp;
struct node
{
ll sum,flag;
}tree[N*4];
void pushup(int rt)
{
tree[rt].sum=tree[lson].sum+tree[rson].sum;
}
void pushdown(int rt,int l,int r)
{
if(l==r)
return ;
if(tree[rt].flag)
{
tree[lson].sum+=tree[rt].flag*(long long)(mid-l+1);
tree[rson].sum+=tree[rt].flag*(long long)(r-mid);
tree[lson].flag+=tree[rt].flag;
tree[rson].flag+=tree[rt].flag;
tree[rt].flag=0LL;
}
}
void ins(int rt,int l,int r,int L,int R,long long vl)
{
if(l==L&&r==R)
{
tree[rt].sum+=(ll)(r-l+1)*vl;
tree[rt].flag+=vl;
return ;
}
pushdown(rt,l,r);
if(mid>=R)
ins(lson,l,mid,L,R,vl);
else if(mid<L)
ins(rson,mid+1,r,L,R,vl);
else
ins(lson,l,mid,L,mid,vl),ins(rson,mid+1,r,mid+1,R,vl);
pushup(rt);
}
long long query(int rt,int l,int r,int L,int R)
{
if(l==L&&r==R)
return tree[rt].sum;
pushdown(rt,l,r);
if(mid>=R)
return query(lson,l,mid,L,R);
else if(mid<L)
return query(rson,mid+1,r,L,R);
else
return query(lson,l,mid,L,mid)+query(rson,mid+1,r,mid+1,R);
}
struct que
{
int l,r;
ll value;
int lei;
}q[N];
int ans[N],m,n;
int id[N],maxv=-500050,minv=500050;
void solve(int l,int r,int L,int R)
{
if(l>r)
return ;
if(L==R)
{
for(int i=l;i<=r;i++)
if(q[id[i]].lei==2)
ans[id[i]]=L;
return ;
}
int md=L+(R-L)/2;
for(int i=l;i<=r;i++)
{
if(q[id[i]].lei==1)
{
if(q[id[i]].value>md) ins(1,1,n,q[id[i]].l,q[id[i]].r,1),rp.push_back(id[i]);
else lp.push_back(id[i]);
}
else
{
ll c=query(1,1,n,q[id[i]].l,q[id[i]].r);
if(c>=q[id[i]].value)
rp.push_back(id[i]);
else
lp.push_back(id[i]),q[id[i]].value-=c;
}
}
for(int i=l;i<=r;i++)
if(q[id[i]].lei==1)
if(q[id[i]].value>md)
ins(1,1,n,q[id[i]].l,q[id[i]].r,-1);
int x=lp.size();
for(int i=0;i<lp.size();i++)
id[i+l]=lp[i];
for(int i=0;i<rp.size();i++)
id[i+l+x]=rp[i];
lp.clear(),rp.clear();
solve(l,l+x-1,L,md);
solve(l+x,r,md+1,R);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d%lld",&q[i].lei,&q[i].l,&q[i].r,&q[i].value),id[i]=i,maxv= q[i].lei==1?max(maxv,(int)q[i].value):maxv,minv= q[i].lei==1?min(minv,(int)q[i].value):minv;
solve(1,m,minv,maxv);
for(int i=1;i<=m;i++)
if(q[i].lei==2)
printf("%d\n",ans[i]);
}
bzoj2738: 矩阵乘法
嗯矩阵乘法,得先写个模板,再写快速幂,准备好纸笔准备推矩阵,然后发现
“给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。”。。。。。。。。。。。
这个题所有的“修改”只有开始的赋值。相当于所有修改没时序,直接对所有询问二分就好了
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N=550;
const int Q=60050;
int n,h,ans[Q],qq;
struct aa
{
int vl,x,y;
}a[N*N];
int cmp(aa a1,aa a2)
{
return a1.vl<a2.vl;
}
struct que
{
int x1,x2,y1,y2;
long long k;
}q[Q];
long long c[N][N];
int lowbit(int x)
{
return x&(-x);
}
void plu(int x,int y,long long val)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j))
c[i][j]+=val;
}
long long query(int end1,int end2)
{
long long sum=0;
for(int i=end1;i>0;i-=lowbit(i))
for(int j=end2;j>0;j-=lowbit(j))
sum+=c[i][j];
return sum;
}
int cnt,tot;
void add(aa x,int f)
{
plu(x.x,x.y,(long long)f);
}
long long qiu(int x1,int y1,int x2,int y2)
{
long long sum=0;
sum+=query(x1-1,y1-1);
sum-=query(x1-1,y2);
sum-=query(x2,y1-1);
sum+=query(x2,y2);
return sum;
}
int id[N*N],tmp[N*N];
void solve(int l,int r,int L,int R)
{
if(l>r)
return ;
if(L==R)
{
for(int i=l;i<=r;i++)
ans[id[i]]=L;
return ;
}
int md=L+(R-L)/2;
while(a[cnt+1].vl<=md&&cnt<h) add(a[++cnt],1);
while(a[cnt].vl>md) add(a[cnt--],-1);
int cn1=0;
for(int i=l;i<=r;i++)
if(qiu(q[id[i]].x1,q[id[i]].y1,q[id[i]].x2,q[id[i]].y2)>=q[id[i]].k)
cn1++;
int l1=l,l2=l+cn1;
for(int i=l;i<=r;i++)
{
if(qiu(q[id[i]].x1,q[id[i]].y1,q[id[i]].x2,q[id[i]].y2)<q[id[i]].k)
tmp[l2++]=id[i];
else
tmp[l1++]=id[i];
}
for(int i=l;i<=r;i++)
id[i]=tmp[i];
solve(l,l1-1,L,md);
solve(l1,l2-1,md+1,R);
}
int minx=1000000000,maxx=0;
int main()
{
scanf("%d%d",&n,&qq);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[++h].vl),a[h].x=i,a[h].y=j,maxx=max(maxx,a[h].vl),minx=min(minx,a[h].vl);
sort(a+1,a+h+1,cmp);
for(int i=1;i<=qq;i++)
scanf("%d%d%d%d%d",&q[i].x1,&q[i].y1,&q[i].x2,&q[i].y2,&q[i].k),id[i]=i;
solve(1,qq,minx,maxx+1);
for(int i=1;i<=qq;i++)
printf("%d\n",ans[i]);
}
bzoj1901: Zju2112 Dynamic Rankings
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N=30050;
int n,m;
int a[N],c[N];
struct que
{
int l,r,k,lei;
}q[N];
char op;
int cnt;
int lowbit(int x)
{
return x&(-x);
}
void plu(int pos,int x)
{
while(pos<=n)
c[pos]+=x,pos+=lowbit(pos);
}
int get(int end)
{
int sum=0;
while(end>0)
sum+=c[end],end-=lowbit(end);
return sum;
}
int query(que a)
{
return get(a.r)-get(a.l-1);
}
int tmp[N];
int zhuan[N],id[N],ans[N];
bool mark[N];
void solve(int l,int r,int L,int R)
{
if(l>r)
return ;
if(L==R)
{
for(int i=l;i<=r;i++)
if(q[id[i]].lei==3)
ans[id[i]]=L;
return ;
}
int md=L+(R-L)/2;
for(int i=l;i<=r;i++)
{
if(q[id[i]].lei==1&&q[id[i]].k<=md) plu(q[id[i]].l,1);
else if(q[id[i]].lei==2&&q[id[i]].k<=md) plu(q[id[i]].l,-1);
else if(q[id[i]].lei==3) tmp[i]=query(q[id[i]]);
}
for(int i=l;i<=r;i++)
{
if(q[id[i]].lei==1&&q[id[i]].k<=md) plu(q[id[i]].l,-1);
else if(q[id[i]].lei==2&&q[id[i]].k<=md) plu(q[id[i]].l,1);
}
int cn=0;
for(int i=l;i<=r;i++)
{
if(q[id[i]].lei==3)
{
if(tmp[i]>=q[id[i]].k) cn++,mark[i]=1;
else q[id[i]].k-=tmp[i],mark[i]=0;
}
else
{
if(q[id[i]].k<=md)
cn++,mark[i]=1;
else
mark[i]=0;
}
}
int l2=l+cn,l1=l;
for(int i=l;i<=r;i++)
{
if(mark[i]) zhuan[l1++]=id[i];
else zhuan[l2++]=id[i];
}
for(int i=l;i<=r;i++)
id[i]=zhuan[i];
solve(l,l1-1,L,md);
solve(l1,r,md+1,R);
}
int ll,rr,vl;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),q[++cnt].lei=1,q[cnt].l=i,q[cnt].k=a[i];
for(int i=1;i<=m;i++)
{
while(scanf("%c",&op)&&op!='C'&&op!='Q');
if(op=='C')
{
int pos,vl;
scanf("%d%d",&pos,&vl);
q[++cnt].lei=2,q[cnt].l=pos,q[cnt].k=a[pos];
q[++cnt].lei=1,q[cnt].l=pos,q[cnt].k=vl;
a[pos]=vl;
}
else
scanf("%d%d%d",&ll,&rr,&vl),q[++cnt].lei=3,q[cnt].l=ll,q[cnt].r=rr,q[cnt].k=vl;
}
for(int i=1;i<=cnt;i++)
id[i]=i;
solve(1,cnt,0,0x3f3f3f3f);
for(int i=1;i<=cnt;i++)
if(q[i].lei==3)
printf("%d\n",ans[i]);
}