BZOJ4552: [Tjoi2016&Heoi2016]排序
4552: [Tjoi2016&Heoi2016]排序
Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 2554 Solved: 1302
[Submit][Status][Discuss]
Description
在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题
,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排
序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q
位置上的数字。
Input
输入数据的第一行为两个整数n和m。n表示序列的长度,m表示局部排序的次数。1 <= n, m <= 10^5第二行为n个整
数,表示1到n的一个全排列。接下来输入m行,每一行有三个整数op, l, r, op为0代表升序排序,op为1代表降序
排序, l, r 表示排序的区间。最后输入一个整数q,q表示排序完之后询问的位置, 1 <= q <= n。1 <= n <= 10^5
,1 <= m <= 10^5
Output
输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第q位置上的数字。
Sample Input
6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3
Sample Output
5
HINT
Source
题解:线段树合并与分裂模板题???这题有个非常巧妙的二分答案转化为01序列的线段树做法,也相对好写,但多了一层log。然后快一点的做法是先建立N颗权值线段树,动态开点,记录每个区间下节点个数。一段区间排序就是将这段区间合并。但是前后的区间合并会产生交集,从而就需要分裂,就是我们要取出l,r这段区间,在左右两侧的能延伸进来的线段都要分裂。就对于左边来说l所属的那个区间如果左端点小于l那么就要分开,也就是维护一些不相交的线段,且他们的并是1~n,那么之前几个月虽然这类题没碰过但是听到了很多讨论,肯定是用个set啦,就按照右端点排序,二分一下就可以找到一个点所属的线段。然后就像权值线段树那样查找第k个元素就好了。不过因为有递增递减两种序列,那么就是如果是递减就先减右区间,递增先减左区间。以上内容都是我学着这篇代码理解的。http://www.cnblogs.com/Gloid/p/10204585.html
#include<bits/stdc++.h> #define pb push_back #define mp make_pair #define ll long long using namespace std; const int maxn=1e5+7; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m; struct node { int l,r,num; }no[maxn*80]; int state[maxn],root[maxn]; struct seg { int l,r; seg(){}; seg(int _l,int _r){l=_l;r=_r;} friend bool operator<(seg a,seg b){return a.r<b.r;} }; set<seg>st; int cnt; void insertt(int &x,int l,int r,int pl) { if(!x)x=++cnt;no[x].num++; if(l==r)return; int mid=(l+r)>>1; if(pl<=mid)insertt(no[x].l,l,mid,pl); else insertt(no[x].r,mid+1,r,pl); } void split(int &x,int &y,int k,int op) { if(!x)return; y=++cnt;no[y].num=no[x].num-k;no[x].num=k; if(op==0) { if(no[no[x].l].num==k){ no[y].r=no[x].r;no[x].r=0; return; } else if(no[no[x].l].num>k){ no[y].r=no[x].r;no[x].r=0; split(no[x].l,no[y].l,k,op); } else { split(no[x].r,no[y].r,k-no[no[x].l].num,op); } } else { if(no[no[x].r].num==k){ no[y].l=no[x].l;no[x].l=0; return; } else if(no[no[x].r].num>k){ no[y].l=no[x].l;no[x].l=0; split(no[x].r,no[y].r,k,op); } else { split(no[x].l,no[y].l,k-no[no[x].r].num,op); } } } void merge(int &x,int &y,int l,int r) { if(!x||!y){x|=y;return;} no[x].num+=no[y].num; if(l==r)return; int mid=(l+r)>>1; merge(no[x].l,no[y].l,l,mid); merge(no[x].r,no[y].r,mid+1,r); } int query(int x,int l,int r,int pl,int op) { if(l==r)return l; int mid=(l+r)>>1; //cout<<l<<" "<<r<<endl; if(op==0) { if(no[no[x].l].num>=pl)return query(no[x].l,l,mid,pl,op); else return query(no[x].r,mid+1,r,pl-no[no[x].l].num,op); } else { if(no[no[x].r].num>=pl)return query(no[x].r,mid+1,r,pl,op); else return query(no[x].l,l,mid,pl-no[no[x].r].num,op); } } set<seg>::iterator it; int main() { n=read();m=read(); int tmp; for(int i=1;i<=n;i++){ tmp=read(); insertt(root[i],1,n,tmp);st.insert(seg(i,i)); } int op,l,r; while(m--) { op=read();l=read();r=read(); it=st.lower_bound(seg(l,l)); if((*it).l<l) { split(root[(*it).l],root[l],l-(*it).l,state[(*it).l]); int L=(*it).l,R=(*it).r; st.erase(it); st.insert(seg(L,l-1)); st.insert(seg(l,R));state[l]=state[L]; } it=st.lower_bound(seg(r,r)); if(it!=st.end()&&(*it).l<=r&&(*it).r>r) { split(root[(*it).l],root[r+1],r-(*it).l+1,state[(*it).l]); int L=(*it).l,R=(*it).r; st.erase(it); st.insert(seg(L,r));st.insert(seg(r+1,R)); state[r+1]=state[L]; } it=st.lower_bound(seg(l,l));it++; while(it!=st.end()&&(*it).r<=r) { merge(root[l],root[(*it).l],1,n);it++; } it=st.lower_bound(seg(l,l)); while(it!=st.end()&&(*it).r<=r){st.erase(it);it=st.lower_bound(seg(l,l));} st.insert(seg(l,r)); state[l]=op; }//cout<<cnt<<endl; tmp=read(); it=st.lower_bound(seg(tmp,tmp)); //cout<<(*it).l<<" "<<(*it).r<<"\n"; //cout<<state[(*it).l]<<"\n"; cout<<query(root[(*it).l],1,n,tmp-(*it).l+1,state[(*it).l])<<"\n"; }
另外我总感觉这数据有点弱。