【codeforces 1208D】 Restore Permutation(線段樹)

題面

題意:

一個長度爲nn的排列aa,現在定義pip_i爲數組aa中下標小於等於ii並且小於aia_i的數字的和。現在給定pp,求aa

思路:

首先可以肯定的是,pp中最後一個00出現的位置posposaa中一定是11。我們可以反證:
假設aposa_{pos}不爲11,假設11pospos之前,那麼ppos1p_{pos} \ge 1;假設11pospos之後,那麼pospos一定不是最後一個00出現的位置,因爲當apos=1a_{pos}=1,那麼pposp_{pos}一定等於00,因此最後一個00出現的位置一定是11,那麼我們把11放到pospos處。並且把ii之後的pjp_{j}都減去11.

其實對於從22nn的處理方法都一樣,對於第ii次操作,找到pp中最後一個爲00的位置pospos,令aposa_{pos}ii,然後對於所有的iposi \ge pos,讓piip_{i}-i。最終即可得到aa數組。對於上面的操作,可以用線段樹。

線段樹尋找最後一個00的位置:從根節點出發,先看右子樹的最小值是否爲00,如果是,去查右子樹,否則查左子樹。
最終複雜度O(n(log(n))2)O(n \cdot (log(n))^2)

代碼:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+100;
const int mod = 1e9+7;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll llINF = 0x3f3f3f3f3f3f3f3f;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fep(i,a,b) for(int i=(a);i>=(b);i--)
inline bool read(ll &num) {
    char in;bool IsN=false;
    in=getchar();
    if(in==EOF) return false;
    while(in!='-'&&(in<'0'||in>'9')) in=getchar();
    if(in=='-'){ IsN=true;num=0;}
    else num=in-'0';
    while(in=getchar(),in>='0'&&in<='9'){
            num*=10,num+=in-'0';
    } 
    if(IsN) num=-num;
    return true;
}
ll minn[N],sum[N],tag[N],p[N],a[N],n;
void pushup(ll rt){
    minn[rt]=min(minn[rt<<1|1],minn[rt<<1]);
    return ;
}
void pushdown(ll rt,ll l,ll r){
    ll mid=(l+r)>>1;
    tag[rt<<1]+=tag[rt];
    tag[rt<<1|1]+=tag[rt];
    minn[rt<<1]+=tag[rt];
    minn[rt<<1|1]+=tag[rt];
    tag[rt]=0;
}
void buildtree(ll rt,ll l,ll r){
    tag[rt]=0;
    if(l==r){
        minn[rt]=p[l];
        return ;
    }
    int mid=(l+r)>>1;
    buildtree(rt<<1,l,mid);
    buildtree(rt<<1|1,mid+1,r);
    pushup(rt);
}
void modify(ll nl,ll nr,ll l,ll r,ll rt,ll k){//區間加減
    if(nl<=l&&r<=nr){
        tag[rt]+=k;
        minn[rt]+=k;
        return ;
    }
    ll mid=(l+r)>>1;
    if(nl<=mid) modify(nl,nr,l,mid,rt<<1,k);
    if(nr>mid) modify(nl,nr,mid+1,r,rt<<1|1,k);
    pushup(rt);
}
ll ask(ll rt,ll l,ll r,ll nl,ll nr){//查詢區間最小值
    if(nl<=l&&r<=nr){
        return minn[rt];
    }
    pushdown(rt,l,r);
    ll mid=(l+r)>>1;
    ll ans=9999999999;
    if(nl<=mid) ans=min(ans,ask(rt<<1,l,mid,nl,nr));
    if(nr>mid) ans=min(ans,ask(rt<<1|1,mid+1,r,nl,nr));
    return ans;
}
ll query(ll rt,ll l,ll r){//查找最後一個0的位置
    if(l==r){
        return l;
    }
    ll mid=(l+r)>>1;
    if(ask(1,1,n,mid+1,r)==0) return query(rt<<1|1,mid+1,r);
    else return query(rt<<1,l,mid);
    pushup(rt);
}
int main(){
    read(n);
    rep(i,1,n) read(p[i]);
    buildtree(1,1,n);
    rep(i,1,n){
        int id=query(1,1,n);
        a[id]=i;
        modify(id+1,n,1,n,1,-i);
        modify(id,id,1,n,1,100000000000+i);//找到位置後把這個變成大數
    }
    rep(i,1,n) cout<<a[i]<<' ';
    cout<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章