1. 程式人生 > >BZOJ5057 : 區間k小值5

BZOJ5057 : 區間k小值5

分治 tor 範圍 lin lap 順序 amp typedef 二分

整體二分,按時間順序依次考慮對於權值落在$[l,r]$內的所有操作。

對於每個修改操作,若權值範圍完全包含了$[l,r]$,那麽在更深層的分治中它都完全包含它,對每個詢問的貢獻是定值,因此在當前層將貢獻及時加給後面的每個詢問即可。否則將該修改操作分裂成最多$2$個子操作,並往下遞歸分治。處理貢獻均可以用樹狀數組實現。

對於每個詢問,求出對應區間內部的和,與$k$進行比較,來決定往左還是往右遞歸。

時間復雜度$O(m\log^2n)$。

#include<cstdio>
#include<vector>
using namespace std;
typedef long long ll;
const int N=30010;
int n,m,i,T;ll e[N][5],pre[N];vector<int>q;
inline int min(int a,int b){return a<b?a:b;}
inline int max(int a,int b){return a>b?a:b;}
struct BIT{
  ll a[N],b[N];int v[N];
  void modify(int x,ll p){for(int i=x;i<=n;i+=i&-i)if(v[i]<T)v[i]=T,a[i]=p,b[i]=p*(x-1);else a[i]+=p,b[i]+=p*(x-1);}
  ll ask(ll x){
    ll t0=0,t1=0;
    for(int i=x;i;i-=i&-i)if(v[i]==T)t0+=a[i],t1+=b[i];
    return x*t0-t1;
  }
  void add(int x,int y,ll p){modify(x,p),modify(y+1,-p);}
  ll sum(int x,int y){return ask(y)-ask(x-1);}
}bit0,bit1;
void solve(int l,int r,vector<int>q){
  if(!q.size())return;
  if(l==r){
    for(int i=0;i<q.size();i++)if(e[q[i]][0]==2)e[q[i]][4]=l;
    return;
  }
  int mid=(l+r)>>1;vector<int>ql,qr;
  T++;
  for(int i=0;i<q.size();i++){
    int x=q[i],A=e[x][1],B=e[x][2],C=e[x][3],D=e[x][4];
    if(e[x][0]==1){
      if(C<=l&&r<=D)bit0.add(A,B,1);
      else{
        int c=max(C,l),d=min(D,mid);
        if(c<=d)bit1.add(A,B,d-c+1);
        if(C<=mid)ql.push_back(x);
        if(D>mid)qr.push_back(x);
      }
    }else{
      pre[x]+=bit0.sum(A,B);
      ll tmp=pre[x]*(mid-l+1)+bit1.sum(A,B);
      if(tmp>=e[x][3])ql.push_back(x);else e[x][3]-=tmp,qr.push_back(x);
    }
  }
  solve(l,mid,ql),solve(mid+1,r,qr);
}
int main(){
  scanf("%d%d",&n,&m);
  for(i=1;i<=m;i++){
    scanf("%lld%lld%lld%lld",&e[i][0],&e[i][1],&e[i][2],&e[i][3]);
    if(e[i][0]==1)scanf("%lld",&e[i][4]);
    q.push_back(i);
  }
  solve(1,n,q);
  for(i=1;i<=m;i++)if(e[i][0]==2)printf("%lld\n",e[i][4]);
  return 0;
}

  

BZOJ5057 : 區間k小值5