• SCOI2014 方伯伯的OJ 和 NOIP2017 列队


    方伯伯的 OJ

    方伯伯正在做他的 OJ。现在他在处理 OJ 上的用户排名问题。

    OJ 上注册了 (n) 个用户,编号为 (1 sim n),一开始他们按照编号排名。方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和编号:

    1. 操作格式为 1 x y,意味着将编号为 (x) 的用户编号改为 (y),而排名不变,执行完该操作后需要输出该用户在排名中的位置,数据保证 (x) 必然出现在排名中,同时 (y) 是一个当前不在排名中的编号。
    2. 操作格式为 2 x,意味着将编号为 (x) 的用户的排名提升到第一位,执行完该操作后需要输出执行该操作前编号为 (x) 用户的排名。
    3. 操作格式为 3 x,意味着将编号为 (x) 的用户的排名降到最后一位,执行完该操作后需要输出执行该操作前编号为 (x) 用户的排名。
    4. 操作格式为 4 k,意味着查询当前排名为 (k) 的用户编号,执行完该操作后需要输出当前操作用户的编号。
      但同时为了防止别人监听自己的工作,方伯伯对他的操作进行了加密,即将四种操作的格式分别改为了:
    1 x+a y+a
    2 x+a
    3 x+a
    4 k+a
    

    其中 (a) 为上一次操作得到的输出,一开始 (a=0)

    对于 (100 \%) 的数据,(1 leq n leq 10^8, 1 leq m leq 10^5)

    题解

    这题只需要维护两个映射就行了,它们分别是:

    1. splay节点到编号区间,这个直接存在splay节点上就行了。

    2. 编号区间到splay节点,这个用map套pair<int,int>就行了。

    但是这道题只会把单个元素移动到开头结尾,所以这样做有些大材小用了。

    https://www.cnblogs.com/ZH-comld/p/9715245.html
    https://www.luogu.com.cn/blog/a23333/scoi2014-fang-bo-bo-di-oj-xian-duan-shu-ping-heng-shu

    只需要用动态开点线段树维护([1-m,n+m])这些可能用到的位置的空缺,然后用两个map维护标号到位置和位置到标号的映射就行了。

    时间复杂度(O(mlog n))

    CO int N=1e5+10;
    int L,R;
    map<int,int> pos,idx;
    int root,tot;
    int lc[N*30],rc[N*30],sum[N*30];
    
    #define mid ((l+r)>>1)
    void insert(int&x,int l,int r,int p){
    	if(!x) x=++tot;
    	++sum[x];
    	if(l==r) return;
    	if(p<=mid) insert(lc[x],l,mid,p);
    	else insert(rc[x],mid+1,r,p);
    }
    int query(int x,int l,int r,int p){
    	if(!sum[x] or l==r) return 0;
    	if(p<=mid) return query(lc[x],l,mid,p);
    	else return sum[lc[x]]+query(rc[x],mid+1,r,p);
    }
    int find(int x,int l,int r,int k){
    	if(l==r) return l;
    	int num=max(0,min(mid,R)-max(l,L)+1-sum[lc[x]]);
    	if(num>=k) return find(lc[x],l,mid,k);
    	else return find(rc[x],mid+1,r,k-num);
    }
    #undef mid
    
    int main(){
    	int n=read<int>(),m=read<int>();
    	L=1,R=n;
    	int ans=0;
    	for(int i=1;i<=m;++i){
    		int opt=read<int>();
    		if(opt==1){
    			int x=read<int>()-ans,y=read<int>()-ans;
    			int p=pos.count(x)?pos[x]:x;
    			ans=p-L+1-query(root,1-m,n+m,p);
    			pos[y]=p,idx[p]=y,
    			printf("%d
    ",ans);
    		}
    		else if(opt==2){
    			int x=read<int>()-ans;
    			int p=pos.count(x)?pos[x]:x;
    			ans=p-L+1-query(root,1-m,n+m,p);
    			insert(root,1-m,n+m,p);
    			pos[x]=--L,idx[L]=x;
    			printf("%d
    ",ans);
    		}
    		else if(opt==3){
    			int x=read<int>()-ans;
    			int p=pos.count(x)?pos[x]:x;
    			ans=p-L+1-query(root,1-m,n+m,p);
    			insert(root,1-m,n+m,p);
    			pos[x]=++R,idx[R]=x;
    			printf("%d
    ",ans);
    		}
    		else{
    			int k=read<int>()-ans;
    			int p=find(root,1-m,n+m,k);
    			ans=idx.count(p)?idx[p]:p;
    			printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }
    

    列队

    Sylvia 是一个热爱学习的女孩子。

    前段时间, Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。 Sylvia 所在的方阵中有 (n imes m) 名学生,方阵的行数为 (n),列数为 (m)

    为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中的学生从 (1)(n imes m) 编上了号码(参见后面的样例)。即:初始时,第 (i) 行第 (j) 列的学生的编号是 ((i − 1) imes m + j)

    然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天中,一共发生了 (q) 件这样的离队事件。每一次离队事件可以用数对 ((x, y) (1le xle n, 1le yle m)) 描述, 表示第 (x) 行第 (y) 列的学生离队。

    在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达这样的两条指令:

    1. 向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条指令之后,空位在第 (x) 行第 (m) 列。

    2. 向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条指令之后,空位在第 (n) 行第 (m) 列。

    教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后,下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 (n) 行第 (m) 列一个空位,这时这个学生会自然地填补到这个位置。

    因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学的编号是多少。

    注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后方阵中同学的编号可能是乱序的。

    (n,m,q leq 3 imes 10^5)

    题解

    https://www.luogu.com.cn/blog/rqy/solution-p3960

    我们观察每一行除了最后一个数之外的数在操作中是如何变化的。

    如果出队在((x,y)),那么第(x)行(除了最后一个数)会弹出第(y)个数,它后面的数依次左移,再在最后插入一个数(就是最后一列当前的第(x)个数);然后,对于最后一列,我们要弹出第(x)个数(插入到第(x)行),再在最后插入一个数(就是刚出队的那个)。

    简单来说,就是第(x)行中间删一个,末尾加一个。最后一列中间删一个,末尾加一个。

    同样有高端splay做法。由于这题我们不需要编号到节点的映射,所以要简单很多。我们只需要对每一行和最后一列维护splay节点到编号区间的映射即可。

    同样这题只会把元素移动到结尾,所以直接动态开点线段树维护空缺,然后map维护位置到标号的映射就行了。

    然而……十年OI一场空,不开long long见祖宗。说起来我两次写这题都忘了开long long了。

    CO int N=3e5+10;
    int R[N];
    map<int,int64> idx[N];
    int root[N],tot;
    int lc[N*40],rc[N*40],sum[N*40];
    
    #define mid ((l+r)>>1)
    void insert(int&x,int l,int r,int p){
    	if(!x) x=++tot;
    	++sum[x];
    	if(l==r) return;
    	if(p<=mid) insert(lc[x],l,mid,p);
    	else insert(rc[x],mid+1,r,p);
    }
    int find(int x,int l,int r,int k,int R){
    	if(l==r) return l;
    	int num=max(0,min(mid,R)-l+1-sum[lc[x]]);
    	if(num>=k) return find(lc[x],l,mid,k,R);
    	else return find(rc[x],mid+1,r,k-num,R);
    }
    #undef mid
    
    int main(){
    	int n=read<int>(),m=read<int>(),Q=read<int>();
    	fill(R+1,R+n+1,m-1),R[n+1]=n;
    	for(int i=1;i<=Q;++i){
    		int x=read<int>(),y=read<int>();
    		if(y==m){
    			int p=find(root[n+1],1,n+Q,x,R[n+1]);
    			int64 u=idx[n+1].count(p)?idx[n+1][p]:(int64)p*m;
    			printf("%lld
    ",u);
    			insert(root[n+1],1,n+Q,p);
    			idx[n+1][++R[n+1]]=u;
    			continue;
    		}
    		int p=find(root[x],1,m+Q,y,R[x]);
    		int64 u=idx[x].count(p)?idx[x][p]:(int64)(x-1)*m+p;
    		printf("%lld
    ",u);
    		int q=find(root[n+1],1,n+Q,x,R[n+1]);
    		int64 v=idx[n+1].count(q)?idx[n+1][q]:(int64)q*m;
    		insert(root[x],1,m+Q,p);
    		idx[x][++R[x]]=v;
    		insert(root[n+1],1,n+Q,q);
    		idx[n+1][++R[n+1]]=u;
    	}
    	return 0;
    }
    
  • 相关阅读:
    [CodeForces
    [CodeChef]RIN(最小割)
    [Bzoj3894]文理分科(最小割)
    [Poj3469]Dual Core CPU(最小割)
    MySQL- 锁(3)
    MySQL- 锁(1)
    MySQL- 锁(2)
    MySQL-中文全文检索
    Solr
    多线程编程-之并发编程:同步容器
  • 原文地址:https://www.cnblogs.com/autoint/p/13070999.html
Copyright © 2020-2023  润新知