Jzoj4605 排序
2017.10.21更新,下面是原本的咸鱼做法
我们发现询问只有一个,我们考虑二分这个最后答案
我们将序列中所有大于mid的值变成1,否则变成0,那么,排序就变成了将序列的01放在头或尾,可以用线段树区间修改做到
#include<stdio.h>
#include<algorithm>
#define mid (l+r>>1)
#define ls x<<1
#define rs x<<1|1
using namespace std;
int s[400040][2],t[400040],w[100010],n,m;
int G[100010][3],q;
void pushdown(int len,int x){
if(t[x]){
t[ls]=t[rs]=t[x];
int d=t[x]-1;
s[ls][d]=len-(len>>1);
s[rs][d]=len>>1;
s[ls][d^1]=s[rs][d^1]=0;
t[x]=0;
}
}
void build(int l,int r,int x,int k){
if(l==r){
s[x][0]=s[x][1]=t[x]=0;
s[x][w[l]>=k]=1; return;
}
build(l,mid,ls,k);
build(mid+1,r,rs,k);
s[x][0]=s[ls][0]+s[rs][0];
s[x][1]=s[ls][1]+s[rs][1];
t[x]=0;
}
int query(int l,int r,int x,int L,int R){
if(L<=l && r<=R){ return s[x][0]; }
int ans=0,m=mid; pushdown(r-l+1,x);
if(L<=m) ans+=query(l,m,ls,L,R);
if(m<R) ans+=query(m+1,r,rs,L,R);
return ans;
}
void update(int l,int r,int x,int L,int R,int p){
if(L>R) return;
if(L<=l && r<=R){
t[x]=p+1; s[x][p]=r-l+1;
s[x][!p]=0; return;
}
int m=mid; pushdown(r-l+1,x);
if(L<=m) update(l,m,ls,L,R,p);
if(m<R) update(m+1,r,rs,L,R,p);
s[x][0]=s[ls][0]+s[rs][0];
s[x][1]=s[ls][1]+s[rs][1];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",w+i);
for(int i=0;i<m;++i) scanf("%d%d%d",G[i],G[i]+1,G[i]+2);
scanf("%d",&q);
int l=0,r=n;
for(int _m;l<r;){
_m=l+r+1>>1;
build(1,n,1,_m);
for(int j=0;j<m;++j){
int L=G[j][1],R=G[j][2];
int v=query(1,n,1,L,R);
if(!G[j][0]){
update(1,n,1,L,L+v-1,0);
update(1,n,1,L+v,R,1);
} else {
v=R-L+1-v;
update(1,n,1,L,L+v-1,1);
update(1,n,1,L+v,R,0);
}
}
if(query(1,n,1,q,q)) r=_m-1; else l=_m;
}
printf("%d\n",l);
}
-----------------------------------------------分割线-----------------------------------------------------
然而这道题正解并不是如此(虽然网上大部分都是这么写)
我们考虑,对于一个区间,我们可以用一种可以快速支持分裂和合并的数据结构来维护
没错,我们可以用splay,但是复杂度是nlg^2n,比上面的咸鱼做法还慢
所以我们考虑另一种做法
注意到题目给出的是一个排列而不是序列
所以我们可以用权值线段树来搞定
线段树,均摊lg n合并,合并的方法和可并堆基本就是一样的(可以参考我置顶的文章),不过每次都将两个儿子都合并而已
分裂?这个和可持久化treap一样啊,按照size分开就行了
最后,每颗线段树维护一个reverse标记表示是顺序还是倒序,总复杂度n Lg n
(线段树合并这个东西还是很浪费空间的)
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
#define mid (l+r>>1)
using namespace std;
struct Tree{ int l,r,s; } s[N<<6];
int n,m,cnt=0,rt[N],rev[N];
struct Set{
int s[N<<2],M;
void init(){
for(M=1;M<=n;M<<=1); M--;
for(int i=M+1;i<=n+M;++i) s[i]=1;
for(int i=M;i;--i) s[i]=s[i<<1]+s[i<<1|1];
}
inline void remove(int x){ for(x+=M;x;x>>=1) --s[x]; }
inline void insert(int x){ for(x+=M;x;x>>=1) ++s[x]; }
inline int gPre(int x){
if(s[x+M]) return x;
for(x+=M;x;x>>=1)
if((x&1)&&(s[x^1])){
for(--x;x<=M;x=(s[x<<1|1]?x<<1|1:x<<1));
return x-M;
}
return -1;
}inline int gSuc(int x){
if(s[x+M]) return x;
for(x+=M;x;x>>=1)
if((~x&1)&&(s[x^1])){
for(++x;x<=M;x=(s[x<<1]?x<<1:x<<1|1));
return x-M;
}
return -1;
}
} c;
inline void ps(Tree& x){ x.s=s[x.l].s+s[x.r].s; }
void insert(int l,int r,int& x,int v){
if(!x) x=++cnt; ++s[x].s;
if(l==r) return;
if(v<=mid) insert(l,mid,s[x].l,v);
else insert(mid+1,r,s[x].r,v);
}
void merge(int& x,int y){
if(!x || !y){ x^=y; return; }
merge(s[x].l,s[y].l);
merge(s[x].r,s[y].r);
ps(s[x]);
}
void split(int& x,int& y,int l,int r,int k){
if(k==0){ y=x; x=0; return; } else y=++cnt;
if(s[s[x].l].s>k){
s[y].r=s[x].r; s[x].r=0;
split(s[x].l,s[y].l,l,mid,k);
} else split(s[x].r,s[y].r,mid+1,r,k-s[s[x].l].s);
ps(s[x]); ps(s[y]);
}
void cut(int p){
int r1=c.gPre(p),r2;
if(r1==p) return;
rev[p]=rev[r1]; r2=c.gSuc(p);
if(!rev[p]) split(rt[r1],rt[p],1,n,p-r1);
else split(rt[r1],rt[p],1,n,r2-p),swap(rt[r1],rt[p]);
c.insert(p);
}
void join(int l,int r,int o){
int r1=c.gSuc(l+1),r2=c.gPre(r);
for(;r1<=r2;r1=c.gSuc(r1)){
merge(rt[l],rt[r1]);
c.remove(r1);
}
rev[l]=o;
}
int gVal(int l,int r,int x){
if(l==r) return l;
if(s[s[x].l].s) return gVal(l,mid,s[x].l);
else return gVal(mid+1,r,s[x].r);
}
int main(){
scanf("%d%d",&n,&m);
for(int x,i=1;i<=n;++i){
scanf("%d",&x);
insert(1,n,rt[i],x);
}
c.init();
c.insert(n+1);
for(int o,l,r;m--;){
scanf("%d%d%d",&o,&l,&r);
cut(l); cut(r+1); join(l,r,o);
}
scanf("%d",&m); cut(m); cut(m+1);
printf("%d\n",gVal(1,n,rt[m]));
}