bzoj1552

这道题用splay写 先离散化数据保证按题目所述顺序来写 按原序作为键值建树 维护区间最小值去跑 每次将i的位置 和 n的位置x和y找出来后 将x旋转到root y旋转到x的有儿子 这时y的左子树就是所求区间 找出区间最小值和他的位置之后 旋转最小值到root 这时他左子树的大小+1就是他现处位置 然后将i到最小值位置中间的区间打上标记等待旋转 这样全部处理完就okay了

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();}
    return ans*f;
}
const int M=100505,inf=1000000000;
int n,root,c[M][2],mn[M],pos[M],ans[M],rev[M],fa[M],size[M],v[M],top,s[M];
struct node{int v,pos;}tr[M];
bool cmp(node a,node b){return ((a.v<b.v)||(a.v==b.v&&a.pos<b.pos));}
bool operator<(node a,node b){return a.pos<b.pos;}
void push_up(int k){
    int l=c[k][0],r=c[k][1];
    size[k]=size[l]+size[r]+1;
    pos[k]=k; mn[k]=v[k];
    if(mn[l]<mn[k]){ mn[k]=mn[l]; pos[k]=pos[l];}
    if(mn[r]<mn[k]){ mn[k]=mn[r]; pos[k]=pos[r];}
}
void push_down(int k){
    int l=c[k][0],r=c[k][1];
    rev[k]=0; rev[l]^=1; rev[r]^=1;
    swap(c[k][0],c[k][1]);
}
int build(int l,int r){
    if(l>r) return 0;
    int m=(l+r)>>1;
    size[m]=1; pos[m]=m;
    c[m][0]=build(l,m-1);
    c[m][1]=build(m+1,r);
    for(int i=0;i<2;i++) if(c[m][i]) fa[c[m][i]]=m;
    push_up(m);
    return m;
}
int find(int x,int rank){
    if(rev[x]) push_down(x);
    int l=c[x][0],r=c[x][1];
    if(size[l]+1==rank) return x;
    else if(size[l]>=rank) return find(l,rank);
    else return find(r,rank-size[l]-1);
}
void rotate(int x,int &k){
    int y=fa[x],z=fa[y],l=0,r=1;
    if(c[y][1]==x) l=1,r=0;
    if(y==k) k=x;
    else{if(c[z][0]==y) c[z][0]=x; else c[z][1]=x;}
    fa[x]=z; fa[y]=x; fa[c[x][r]]=y;
    c[y][l]=c[x][r]; c[x][r]=y;
    push_up(x); push_up(y);
}
void splay(int x,int &k){
    top=0;s[++top]=x;
    for(int i=x;fa[i];i=fa[i])
        s[++top]=fa[i];
    for(int i=top;i;i--)
        if(rev[s[i]]) push_down(s[i]);
    while(x!=k){
        int y=fa[x],z=fa[y];
        if(y!=k){
            if(c[z][0]==y^c[y][0]==x) rotate(y,k);
            else rotate(x,k);
        }
        rotate(x,k);
    }
}
int push_ans(int l,int r){
    int x=find(root,l),y=find(root,r+2);
    splay(x,root); splay(y,c[x][1]);
    return pos[c[y][0]];
}
void rever(int l,int r){
    int x=find(root,l),y=find(root,r+2);
    splay(x,root); splay(y,c[x][1]);
    rev[c[y][0]]^=1;
}
int main()
{
    n=read(); mn[0]=inf;
    tr[1].v=tr[n+2].v=inf;
    for(int i=2;i<=n+1;i++) tr[i].v=read(),tr[i].pos=i;
    sort(tr+2,tr+2+n,cmp);
    for(int i=2;i<=n+1;i++) tr[i].v=i-1;
    sort(tr+2,tr+2+n); for(int i=2;i<=n+1;i++) v[i]=tr[i].v;
    root=build(1,n+2);
    for(int i=1;i<=n;i++){
        int x=push_ans(i,n);
        splay(x,root);
        ans[i]=size[c[x][0]];
        rever(i,ans[i]);
    }
    for(int i=1;i<=n;i++){ printf("%d",ans[i]); if(i!=n) printf(" ");}
    return 0;
}

时间: 2024-12-14 18:43:37

bzoj1552的相关文章

【bzoj1552/3506】[Cerc2007]robotic sort splay翻转,区间最值

[bzoj1552/3506][Cerc2007]robotic sort Description Input 输入共两行,第一行为一个整数N,N表示物品的个数,1<=N<=100000.第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号. Output 输出共一行,N个用空格隔开的正整数P1,P2,P3…Pn,(1 < = Pi < = N),Pi表示第i次操作前第i小的物品所在的位置. 注意:如果第i次操作前,第i小的物品己经在正确的位置Pi上,我们将区间[Pi,Pi]

[BZOJ1552][Cerc2007]robotic sort

试题描述 输入 输入共两行,第一行为一个整数N,N表示物品的个数,1<=N<=100000.第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号. 输出 输出共一行,N个用空格隔开的正整数P1,P2,P3-Pn,Pi表示第i次操作前第i小的物品所在的位置. 注意:如果第i次操作前,第i小的物品己经在正确的位置Pi上,我们将区间[Pi,Pi]反转(单个物品). 输入示例 6 3 4 5 1 6 2 输出示例 4 6 4 5 6 6 数据规模及约定 见"输入" 题解 暴力

【BZOJ-1552&amp;3506】robotic sort&amp;排序机械臂 Splay

1552: [Cerc2007]robotic sort Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 806  Solved: 329[Submit][Status][Discuss] Description Input 输入共两行,第一行为一个整数N,N表示物品的个数,1<=N<=100000.第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号. Output 输出共一行,N个用空格隔开的正整数P1,P2,P3…Pn,Pi表示第i次操作

bzoj1552&amp;3506 robotic sort

这道题提醒了我: 1.交之前要删文件 2.v不要打成mn 3.maintain的位置 4.pushdown pushdown pushdown 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #define rep(i,l,r) for(in

[bzoj1552][Cerc2007]robotic sort&amp;&amp;[bzoj3506][Cqoi2014]排序机械臂

非常垃圾的一道平衡树,结果被日了一天.很难受嗷嗷嗷 首先不得不说网上的题解让我这个本来就不熟悉平衡树的彩笔很难受--并不好理解. 还好Sinogi大佬非常的神,一眼就切掉了,而且用更加美妙的解法. 题意在操作时,就是第i次把编号为i-1和编号i的后继分别提到根和根的右儿子,根的右儿子的左子树打上翻转标记. 用外部数组记录原来高度第几大的在平衡树中编号是多少.就可以直接操作了. 注意有相同的高度,离散化时直接按高度第一关键字,编号第二关键字就行了. 还有每次splay要把根到当前节点都pushdo

大神刷题表

9月27日 后缀数组:[wikioi3160]最长公共子串 dp:NOIP2001统计单词个数 后缀自动机:[spoj1812]Longest Common Substring II [wikioi3160]最长公共子串 [spoj7258]Lexicographical Substring Search 扫描线+set:[poj2932]Coneology 扫描线+set+树上删边游戏:[FJOI2013]圆形游戏 结论:[bzoj3706][FJ2014集训]反色刷 最小环:[poj1734

[BZOJ3506] [Cqoi2014] 排序机械臂 (splay)

Description 同OJ1552 Input Output Sample Input Sample Output HINT Source Solution Q:哎不是同一道题吗为什么分两篇博客来写? A:当然是来骗浏览量了233333333,这里还是会放代码的,当然你左转BZOJ1552的题解也行. 1 #include <bits/stdc++.h> 2 using namespace std; 3 struct spaly 4 { 5 int c[2], fa, siz, rev;

[转载]hzwer的bzoj题单

counter: 664BZOJ1601 BZOJ1003 BZOJ1002 BZOJ1192 BZOJ1303 BZOJ1270 BZOJ3039 BZOJ1191 BZOJ1059 BZOJ1202 BZOJ1051 BZOJ1001 BZOJ1588 BZOJ1208 BZOJ1491 BZOJ1084 BZOJ1295 BZOJ3109 BZOJ1085 BZOJ1041 BZOJ1087 BZOJ3038 BZOJ1821 BZOJ1076 BZOJ2321 BZOJ1934 BZOJ