整体二分

ooliver的头像 发布于 14 小时前 9 次阅读 OI


P3834 【模板】可持久化线段树 2

模板,直接看代码:

#include<bits/stdc++.h>
using namespace std;

#define lowbit(x) x&-x
const int N=2e5+5;

int n,m,cnt;
int ans[N],tree[N];
int maxn=INT_MIN,minn=INT_MAX;

struct node{
    int x,y,k,id,op;
}q[N<<1],q1[N<<1],q2[N<<1];

struct BIT{
    int p[N];
    void add(int x,int k){
        for(int i=x;i<=n;i+=lowbit(i)) p[i]+=k;
    }
    int sum(int x){
        int res=0;
        for(int i=x;i>=1;i-=lowbit(i)) res+=p[i];
        return res;
    }
}bit;

void solve(int l,int r,int vl,int vr){
    if(l>r) return;
    if(vl==vr){
        for(int i=l;i<=r;i++)
            if(q[i].op) ans[q[i].id]=vl;
        return;
    }
    int mid=vl+vr>>1;
    int cnt1=0,cnt2=0;
    for(int i=l;i<=r;i++){
        if(!q[i].op){
            if(q[i].x<=mid){
                bit.add(q[i].id,1);
                q1[++cnt1]=q[i];
            }
            else q2[++cnt2]=q[i];
        }
        else{
            int s=bit.sum(q[i].y)-bit.sum(q[i].x-1);
            if(s>=q[i].k) q1[++cnt1]=q[i];
            else{
                q[i].k-=s;
                q2[++cnt2]=q[i];
            }
        }
    }
    for(int i=1;i<=cnt1;i++) if(!q1[i].op) bit.add(q1[i].id,-1);
    for(int i=1;i<=cnt1;i++) q[i+l-1]=q1[i];
    for(int i=1;i<=cnt2;i++) q[i+l+cnt1-1]=q2[i];
    solve(l,l+cnt1-1,vl,mid);
    solve(l+cnt1,r,mid+1,vr);
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        q[++cnt]={x,0,0,i,0};
        minn=min(minn,x),maxn=max(maxn,x);
    }
    for(int i=1;i<=m;i++){
        int x,y,k;
        cin>>x>>y>>k;
        q[++cnt]={x,y,k,i,1};
    }
    solve(1,cnt,minn,maxn);
    for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}

P1527 [国家集训队] 矩阵乘法

上一题的二维版本,把树状数组改成二维的即可,代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define lowbit(x) x&-x
const int N=505,M=5e5+5;

int n,m,cnt;
int ans[M];
int maxn=LONG_LONG_MIN,minn=LONG_LONG_MAX;

struct node{
    int x1,y1,x2,y2,k,id,op;
}q[M],q1[M],q2[M];

struct BIT{
    int p[N][N];
    void add(int x,int y,int k){
        for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=n;j+=lowbit(j)) p[i][j]+=k;
    }
    int sum(int x,int y){
        int res=0;
        for(int i=x;i>=1;i-=lowbit(i)) for(int j=y;j>=1;j-=lowbit(j)) res+=p[i][j];
        return res;
    }
}bit;

void solve(int l,int r,int vl,int vr){
    if(l>r) return;
    if(vl==vr){
        for(int i=l;i<=r;i++)
            if(q[i].op) ans[q[i].id]=vl;
        return;
    }
    int mid=vl+vr>>1;
    int cnt1=0,cnt2=0;
    for(int i=l;i<=r;i++){
        if(!q[i].op){
            if(q[i].id<=mid){
                bit.add(q[i].x1,q[i].y1,1);
                q1[++cnt1]=q[i];
            }
            else q2[++cnt2]=q[i];
        }
        else{
            int s=bit.sum(q[i].x2,q[i].y2)-bit.sum(q[i].x1-1,q[i].y2)-bit.sum(q[i].x2,q[i].y1-1)+bit.sum(q[i].x1-1,q[i].y1-1);
            if(s>=q[i].k) q1[++cnt1]=q[i];
            else{
                q[i].k-=s;
                q2[++cnt2]=q[i];
            }
        }
    }
    for(int i=1;i<=cnt1;i++) if(!q1[i].op) bit.add(q1[i].x1,q1[i].y1,-1);
    for(int i=1;i<=cnt1;i++) q[i+l-1]=q1[i];
    for(int i=1;i<=cnt2;i++) q[i+l+cnt1-1]=q2[i];
    solve(l,l+cnt1-1,vl,mid);
    solve(l+cnt1,r,mid+1,vr);
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
        int x;
        cin>>x;
        q[++cnt]={i,j,0,0,0,x,0};
        minn=min(minn,x),maxn=max(maxn,x);
    }
    for(int i=1;i<=m;i++){
        int x1,y1,x2,y2,k;
        cin>>x1>>y1>>x2>>y2>>k;
        q[++cnt]={x1,y1,x2,y2,k,i,1};
    }
    solve(1,cnt,minn,maxn);
    for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}

P3527 [POI 2011] MET-Meteors

每次询问的答案具有单调性,考虑整体二分。

相对于模板,把树状数组的操作改为区间修改、单点查询,注意在计算时要把每个国家所占所有轨道都计算上。

代码:

#include<bits/stdc++.h>
using namespace std;

#define int unsigned long long
#define lowbit(x) x&-x
const int N=3e5+5;

int n,m,k;
int o[N<<1],ans[N];
vector<int> g[N<<1];

struct node{
    int x,y,a,id;
}rain[N];

struct ask{
    int p,id;
}q[N<<1],q1[N<<1],q2[N<<1];

struct BIT{
    int tr[N<<1];
    void add(int x,int k){
        for(int i=x;i<=m*2;i+=lowbit(i)) tr[i]+=k;
    }
    int sum(int x){
        int res=0;
        for(int i=x;i>=1;i-=lowbit(i)) res+=tr[i];
        return res;
    }
}bit;

int check(int col){
    int res=0;
    for(int v:g[col]) res+=bit.sum(v);
    return res;
}

void solve(int l,int r,int l1,int r1){
    if(l1>r1) return;
    if(l==r){
        for(int i=l1;i<=r1;i++) ans[q[i].id]=l;
        return;
    }
    int mid=l+r>>1;
    for(int i=l;i<=mid;i++) bit.add(rain[i].x,rain[i].a),bit.add(rain[i].y+1,-rain[i].a);
    int cnt1=0,cnt2=0;
    for(int i=l1;i<=r1;i++){
        int k=check(q[i].id);
        if(k>=q[i].p) q1[++cnt1]=q[i];
        else{
            q[i].p-=k;
            q2[++cnt2]=q[i];
        }
    }
    for(int i=l;i<=mid;i++) bit.add(rain[i].x,-rain[i].a),bit.add(rain[i].y+1,rain[i].a);
    for(int i=1;i<=cnt1;i++) q[i+l1-1]=q1[i];
    for(int i=1;i<=cnt2;i++) q[i+l1+cnt1-1]=q2[i];
    solve(l,mid,l1,l1+cnt1-1);
    solve(mid+1,r,l1+cnt1,r1);
}

signed main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>o[i];
        o[i+m]=o[i];
        g[o[i]].push_back(i),g[o[i]].push_back(i+m);
    }
    for(int i=1;i<=n;i++) cin>>q[i].p,q[i].id=i;
    cin>>k;
    for(int i=1;i<=k;i++){
        int x,y,a;
        cin>>x>>y>>a;
        if(x>y) y+=m;
        rain[i]={x,y,a,i};
    }
    solve(1,k+1,1,n);
    for(int i=1;i<=n;i++){
        if(ans[i]>k) cout<<"NIE\n";
        else cout<<ans[i]<<"\n";
    }
    return 0;
}

P3332 [ZJOI2013] K 大数查询

相较于板子发现需要一个支持区间修改和区间查询的数据结构,考虑线段树,其它都差不多了。

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define lc p<<1
#define rc p<<1|1
#define lowbit(x) x&-x
const int N=3e5+5;

int n,m,cnt;
int o[N<<1],ans[N];

struct node{
    int x,y,k,id,op;
}q[N],q1[N<<1],q2[N<<1];

struct SEG{
    struct TREE{
        int l,r,sum,lazy;
        void tag(int k){
            sum+=k*(r-l+1);
            lazy+=k;
        }
    }tr[N<<2];
    void pushup(int p){
        tr[p].sum=tr[lc].sum+tr[rc].sum;
    }
    void pushdown(int p){
        if(tr[p].lazy){
            tr[lc].tag(tr[p].lazy);
            tr[rc].tag(tr[p].lazy);
            tr[p].lazy=0;
        }
    }
    void build(int p,int l,int r){
        tr[p].l=l,tr[p].r=r;
        if(l==r){
            tr[p].sum=0;
            return;
        }
        int mid=l+r>>1;
        build(lc,l,mid);
        build(rc,mid+1,r);
        pushup(p);
    }
    void change(int p,int l,int r,int k){
        if(tr[p].l>=l&&tr[p].r<=r){
            tr[p].tag(k);
            return;
        }
        pushdown(p);
        int mid=tr[p].l+tr[p].r>>1;
        if(l<=mid) change(lc,l,r,k);
        if(r>mid) change(rc,l,r,k);
        pushup(p);
    }
    int check(int p,int l,int r){
        if(tr[p].l>=l&&tr[p].r<=r) return tr[p].sum;
        pushdown(p);
        int mid=tr[p].l+tr[p].r>>1,ans=0;
        if(l<=mid) ans+=check(lc,l,r);
        if(r>mid) ans+=check(rc,l,r);
        return ans;
    }
}tree;

void solve(int l,int r,int l1,int r1){
    if(l1>r1) return;
    if(l==r){
        for(int i=l1;i<=r1;i++) if(q[i].op==2) ans[q[i].id]=l;
        return;
    }
    int mid=l+r>>1;
    int cnt1=0,cnt2=0;
    for(int i=l1;i<=r1;i++){
        if(q[i].op==1){
            if(q[i].k>mid){
                tree.change(1,q[i].x,q[i].y,1);
                q2[++cnt2]=q[i];
            }
            else q1[++cnt1]=q[i];
        }
        else{
            int vis=tree.check(1,q[i].x,q[i].y);
            if(q[i].k>vis){
                q[i].k-=vis;
                q1[++cnt1]=q[i];
            }
            else q2[++cnt2]=q[i];
        }
    }
    for(int i=l1;i<=r1;i++) if(q[i].op==1) if(q[i].k>mid) tree.change(1,q[i].x,q[i].y,-1);
    for(int i=1;i<=cnt1;i++) q[i+l1-1]=q1[i];
    for(int i=1;i<=cnt2;i++) q[i+l1+cnt1-1]=q2[i];
    solve(l,mid,l1,l1+cnt1-1);
    solve(mid+1,r,l1+cnt1,r1);
}

signed main(){
    cin>>n>>m;
    tree.build(1,1,n);
    for(int i=1;i<=m;i++){
        cin>>q[i].op>>q[i].x>>q[i].y>>q[i].k;
        if(q[i].op==2) q[i].id=++cnt;
    }
    solve(-n,n,1,m);
    for(int i=1;i<=cnt;i++) cout<<ans[i]<<"\n";
    return 0;
}

P7424 [THUPC 2017] 天天爱射击

答案明显具有单调性,考虑整体二分。

其余二分答案没啥好说的,一个答案区间,一个处理询问区间,先假设答案区间前一半的子弹都发射,再把询问区间的木板分成两部分,这些都是整体二分的套路。

代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define lowbit(x) x&-x
const int N=5e5+5;

int n,m,cnt;
int a[N],ans[N];

struct node{
    int x,y,k,id;
}q[N],q1[N<<1],q2[N<<1];

struct BIT{
    int tr[N<<1];
    void add(int x,int k){
        for(int i=x;i<N;i+=lowbit(i)) tr[i]+=k;
    }
    int sum(int x){
        int res=0;
        for(int i=x;i>=1;i-=lowbit(i)) res+=tr[i];
        return res;
    }
}bit;

void solve(int l,int r,int l1,int r1){
    if(l1>r1) return;
    if(l==r){
        for(int i=l1;i<=r1;i++) if(q[i].k==1&&q[i].x<=a[l]&&q[i].y>=a[r]) ans[l]++;
        return;
    }
    int mid=l+r>>1;
    int cnt1=0,cnt2=0;
    for(int i=l;i<=mid;i++) bit.add(a[i],1);
    for(int i=l1;i<=r1;i++){
        int vis=bit.sum(q[i].y)-bit.sum(q[i].x-1);
        if(vis<q[i].k){
            q[i].k-=vis;
            q2[++cnt2]=q[i];
        }
        else q1[++cnt1]=q[i];
    }
    for(int i=l;i<=mid;i++) bit.add(a[i],-1);
    for(int i=1;i<=cnt1;i++) q[i+l1-1]=q1[i];
    for(int i=1;i<=cnt2;i++) q[i+l1+cnt1-1]=q2[i];
    solve(l,mid,l1,l1+cnt1-1);
    solve(mid+1,r,l1+cnt1,r1);
}

signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>q[i].x>>q[i].y>>q[i].k;
    for(int i=1;i<=m;i++) cin>>a[i];
    solve(1,m,1,n);
    for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}