整体二分小结

整体二分

        整体二分简单来说就是把所有修改和询问一起二分

  要求询问满足二分性,询问相互独立

     思想就是每次二分到一个答案,将询问划分到左右两个区间

  那么以什么作为划分依据呢?

  取决于操作对左右区间的贡献,如果有贡献就放到左区间 ,否则放入右区间,并将这次贡献的权值减去

  同时,用树状数组或线段树来维护贡献值

  注意每次操作完后要将修改还原

 

例题:

1. 区间第K小

静态 :luogu P3834 

Code :

// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 500010;
const int INF = 1e9+7;

int n,m,cnt; 
int c[maxn];
int ans[maxn];

struct Node{
    int x,y,k,pos,qr;
}q[maxn],q1[maxn],q2[maxn];

void add(int x,int k){
    for(int i=x;i<=n;i+=i&(-i))
        c[i]+=k;
}

int sum(int x){
    int res=0;
    for(int i=x;i>0;i-=i&(-i))
        res+=c[i];
    return res;
}

void solve(int l,int r,int x,int y){
    if(l>r || x>y) return;
    if(l==r)
    {
        for(int i=x;i<=y;i++) if(q[i].qr) ans[q[i].pos]=l;
        return;
    }
    int mid=(l+r)>>1, cnt1=0, cnt2=0;
    for(int i=x;i<=y;i++){
        if(q[i].qr){
            int tmp=sum(q[i].y)-sum(q[i].x-1);
            if(tmp>=q[i].k) q1[++cnt1]=q[i];
            else q[i].k-=tmp,q2[++cnt2]=q[i];
        }else{
            if(q[i].x<=mid) q1[++cnt1]=q[i],add(q[i].pos,q[i].y);
            else q2[++cnt2]=q[i];
        }
    }
    for(int i=1;i<=cnt1;i++) if(!q1[i].qr) add(q1[i].pos,-q1[i].y);
    for(int i=1;i<=cnt1;i++) q[x+i-1]=q1[i];
    for(int i=1;i<=cnt2;i++) q[x+cnt1+i-1]=q2[i];
    solve(l,mid,x,x+cnt1-1); solve(mid+1,r,x+cnt1,y);
}

int read(){ int s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
    n=read(),m=read();
    int x,y,k;
    for(int i=1;i<=n;i++){
        x=read(); q[++cnt]=(Node){x,1,0,i,0};
    }
    for(int i=1;i<=m;i++){
        x=read(),y=read(),k=read();
        q[++cnt]=(Node){x,y,k,i,1};
    }
    
    solve(-INF,INF,1,cnt);
    
    for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}
View Code

动态 :dynamic ranking

Code :

(留坑待填)

 

2. POI  MET-Meteors

   询问是 n 个国家的陨石数量,注意可能爆long long ,如果答案已经超过询问的值要跳出。

Code :

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;

const int maxn = 300030;
const ll INF = 1e9+7;

int n,m,K,cnt;
ll ans[maxn],c[maxn];

struct C{
    int l,r; ll w;
}e[maxn];
struct Q{
    int id; ll k;
}q[maxn],q1[maxn],q2[maxn];

vector<int> ar[maxn];

void add(int x,ll v){
    for(int i=x;i<=m;i+=i&(-i))
        c[i]+=v;
}

ll sum(int x){
    ll res=0;
    for(int i=x;i>0;i-=i&(-i))
        res+=c[i];
    return res;
} 

void mdf(int u,int f){
    if(e[u].l>e[u].r) add(1,f*e[u].w);
    add(e[u].l,f*e[u].w); add(e[u].r+1,-f*e[u].w);
}

void solve(int l,int r,int x,int y){
    if(x>y) return;
    if(l==r){
        for(int i=x;i<=y;i++) ans[q[i].id]=l;
        return;
    }
    int mid=(l+r)>>1,cnt1=0,cnt2=0;
    for(int i=l;i<=mid;i++) mdf(i,1);
    ll tot;
    for(int i=x;i<=y;i++){
        tot=0;
        for(int j=0;j<ar[q[i].id].size();j++){
            tot+=sum(ar[q[i].id][j]);
            if(tot>=q[i].k) break;
        }
        if(tot>=q[i].k) q1[++cnt1]=q[i];
        else {
            q[i].k-=tot;
            q2[++cnt2]=q[i];
        }
    }
    for(int i=l;i<=mid;i++) mdf(i,-1);
    for(int i=1;i<=cnt1;i++) q[i+x-1]=q1[i];
    for(int i=1;i<=cnt2;i++) q[i+x+cnt1-1]=q2[i];
    solve(l,mid,x,x+cnt1-1); solve(mid+1,r,x+cnt1,y);
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++)
        ar[read()].push_back(i);

    for(int i=1;i<=n;i++){
        q[i].k=read(); q[i].id=i;
    }
    K=read();
    for(int i=1;i<=K;i++)
        e[i].l=read(),e[i].r=read(),e[i].w=read();
    ++K; e[K]=(C){1,m,INF};
    
    solve(1,K,1,n);
    
    for(int i=1;i<=n;i++) {
        if(ans[i]==K) printf("NIE\n");
        else printf("%lld\n",ans[i]);
    }
    return 0;
}
View Code

 

posted @ 2018-12-14 21:10  Tartarus_li  阅读(282)  评论(0编辑  收藏  举报