整體二分

HarlemBlog發表於2024-11-25
更新日誌

概念

將所有詢問離線下來,對這個整體進行二分答案。

思路

通用的思路是,考慮對於單個問題,如何二分答案。

然後將所有二分答案壓縮到一次二分答案之中。

更詳細地,我們將所有詢問儲存在一起,每次二分答案時,將詢問分成兩類:當前 mid 已滿足的和未滿足的。

那麼,下一次二分時,答案區間 [l,mid] 需要處理的詢問就只有 mid 已滿足的,反之同理。

同時,通常情況下,如果當前詢問不滿足,應當將其需求減去當前滿足了的數量,讓下一輪計算時只需要計算新增貢獻即可。

當區間縮為一點時,答案固定。

細節

通常情況下,整體二分會涉及到字首和、樹狀陣列、線段樹等,這時候每次遞迴下一輪前都要對其清空。考慮到直接清空複雜度太高,我們只需要把所有進行過操作的部分減回去即可。

另外,在分類詢問時,可以開一個臨時陣列儲存分類結果,最後用它覆蓋原陣列即可。

如果存在無解情況,記得把對應邊界開大 \(1\)

程式碼

void solve(int x,int y,int l,int r){//x,y詢問區間 l,r答案區間
    if(l==r){
        rep(i,x,y)ans[arr[i]]=l;//記錄答案
        return;
    }
    int mid=l+r>>1;
    rep(i,l,mid){
        //統計答案貢獻
    }
    int cnt=0;
    rep(i,x,y){
        ll num=0;
        //計算當前答案
        if(num>=ned[arr[i]]){//可行
            can[i]=true;
            cnt++;
        }else{
            can[i]=false;
            ned[arr[i]]-=num;//減去當前貢獻
        }
    }
    int cnt1=0,cnt2=0;
    //分類
    rep(i,x,y){
        if(can[i]){
            tmp[x+cnt1]=arr[i];
            cnt1++;
        }else{
            tmp[x+cnt+cnt2]=arr[i];
            cnt2++;
        }
    }
    rep(i,x,y)arr[i]=tmp[i];
    rep(i,l,mid){
        //退回貢獻
    }
    solve(x,x+cnt-1,l,mid);solve(x+cnt,y,mid+1,r);//遞迴下一輪
}

修改操作

[待更新]

例題

MET-Meteors

程式碼

前注:非題解,不做詳細講解

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

typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef double db;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
template <typename Type>
using vec=vector<Type>;
template <typename Type>
using grheap=priority_queue<Type>;
template <typename Type>
using lrheap=priority_queue<Type,vector<Type>,greater<Type> >;
#define fir first
#define sec second
#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7/*998244353*/;

const int N=3e5+5;

int n,m,k;
vec<int> plcs[N];
int ned[N];
struct rain{int l,r,a;}rs[N];
int arr[N],tmp[N];
int ans[N];
bool can[N];

struct fenwick{
    ll dat[N*2];
    int lowbit(int x){return x&-x;}
    void add(int x,ll a){
        while(x<=m*2){
            dat[x]+=a;
            x+=lowbit(x);
        }
    }
    ll query(int x){
        ll res=0;
        while(x>0){
            res+=dat[x];
            x-=lowbit(x);
        }
        return res;
    }
}fwt;

void solve(int x,int y,int l,int r){
    if(l==r){
        rep(i,x,y)ans[arr[i]]=l;
        return;
    }
    int mid=l+r>>1;
    rep(i,l,mid){
        fwt.add(rs[i].l,rs[i].a);
        fwt.add(rs[i].r+1,-rs[i].a);
    }
    int cnt=0;
    rep(i,x,y){
        ll num=0;
        for(auto j:plcs[arr[i]]){
            num+=fwt.query(j)+fwt.query(j+m);
            if(num>=ned[arr[i]])break;
        }
        if(num>=ned[arr[i]]){
            can[i]=true;
            cnt++;
        }else{
            can[i]=false;
            ned[arr[i]]-=num;
        }
    }
    int cnt1=0,cnt2=0;
    rep(i,x,y){
        if(can[i]){
            tmp[x+cnt1]=arr[i];
            cnt1++;
        }else{
            tmp[x+cnt+cnt2]=arr[i];
            cnt2++;
        }
    }
    rep(i,x,y)arr[i]=tmp[i];
    rep(i,l,mid){
        fwt.add(rs[i].l,-rs[i].a);
        fwt.add(rs[i].r+1,rs[i].a);
    }
    solve(x,x+cnt-1,l,mid);solve(x+cnt,y,mid+1,r);
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m;
    int o;
    rep(i,1,m){cin>>o;plcs[o].pub(i);}
    rep(i,1,n)cin>>ned[i];
    cin>>k;
    rep(i,1,k){
        cin>>rs[i].l>>rs[i].r>>rs[i].a;
        if(rs[i].r<rs[i].l)rs[i].r+=m;
    }
    rep(i,1,n)arr[i]=i;
    solve(1,n,1,k+1);
    rep(i,1,n){
        if(ans[i]==k+1)cout<<"NIE\n";
        else cout<<ans[i]<<"\n";
    }
    return 0;
}

相關文章