P3988 [SHOI2013] 发牌 题解

Description

\(n\) 张牌,一开始从 \(1\sim n\) 排列,有 \(n\) 次操作,每次把 \(R\) 张牌移到最尾,求每次发牌过后队首的牌,并且删掉这张牌

Solution

考虑用 fhq-treap 求解,因为 fhq-treap 有着强大的处理区间的能力,所以我们直接模拟题面的要求即可:

对于移到最尾的操作,我们考虑用按 siz 分裂的 fhq-treap,数里的权值就是牌的编号,队首即对 rnk=1分裂即可,并且顺便我们可以直接通过转移根节点来实现删牌的操作,可谓是一举两得。

美中不足的就是常数略大,需吸氧

Code

已加入防抄袭片段,请自重

/*If you are full of hope,you will be invincible*/
#include <ctime>
#include <cstdio>
#include <random>
#include <cstring>
#include <iostream>
#include <algorithm>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#include<ext/pb_ds/priority_queue.hpp>
#define ri register int
typedef long long ll;
std::mt19937 hpy(time(nullptr)+(unsigned long long)(new char));
namespace IO{
	constexpr int HL=1<<20;
	char buf[HL],*t1=buf,*t2=buf;
	#ifndef ONLINE_JUDGE
	#define getc() getchar()
	#else
	#define getc() t1==t2&&(t2=(t1=buf)+fread(buf,1,HL,stdin),t1==t2)?EOF:*t1++
	#endif
	template <typename Tp>
	inline void read(Tp &x) {
		x = 0;int f = 1;char ch = getc();
		for(;!isdigit(ch);ch=getc()) if(ch=='-') f = 0;
		for(;isdigit(ch);ch=getc()) x=(x<<3)+(x<<1)+(ch^48);
		x=f?x:-x;
	}
	template <typename Tp,typename ...Rp>
	inline void read(Tp &x,Rp&... rst) {
		read(x),read(rst...);
	}
	char buff[HL],*T=buff;
	void flush(){fwrite(buff,1,T-buff,stdout);T=buff;}
	inline void putc(char ch){if(T==buff+HL)flush();*T++=ch;}
	template<typename Tp>
	inline void print(Tp x){if(x<0)putc('-'),x=-x;if(!x)return putc('0')void();
	static int st[20],tp;while(x)st[++tp]=x%10,x/=10;while(tp)putc(st[tp]^48),--tp;}
	template<typename T,typename ...Args> inline void print(const char ch,T x,Args ...args) {
        print(x,ch);print(args...,ch);
    }
}
using IO::read;
using IO::print;
using IO::putc;
constexpr int inf = 0x3f3f3f3f,N = 7e5 + 10;
struct node{int l,r,siz,key,val;}t[N];
int n,m,cnt,root,R;
inline int newnode(const int x) {return t[++cnt].val = x,t[cnt].key=hpy(),t[cnt].siz=1,cnt;}
inline void update(const int x){t[x].siz = t[t[x].l].siz + t[t[x].r].siz + 1;}
void Split(const int now,const int rnk,int &x,int &y) {
	if(!now) return x=y=0,void();
	if(t[t[now].l].siz < rnk) {
		x = now;
		Split(t[now].r,rnk-t[t[now].l].siz-1,t[now].r,y);
	} else{ 
		y = now;
		Split(t[now].l,rnk,x,t[now].l);
	}
	update(now);
}
int Merge(const int x,const int y) {
	if(!x || !y) return x + y;
	if(t[x].key < t[y].key) {
		t[x].r = Merge(t[x].r,y);
		return update(x),x;
	} else {
		t[y].l = Merge(x,t[y].l);
		return update(y),y;
	}
}

int main() {
   	read(n);
   	for(ri i = 1;i <= n;++i ) root = Merge(root,newnode(i));
   	while(n--) {
   		int rnk,x,y;read(rnk);
   		rnk %= t[root].siz;//前R张牌至少要比总牌数要少(如果多的话相当于是轮回)
   		if(rnk) Split(root,rnk,x,y),root = Merge(y,x);
   		Split(root,1,x,y);
   		print(t[x].val,'\n'),root = y;//转移根节点
   	}
    return IO::flush(),0;
}
posted @ 2021-04-08 19:48  feicheng  阅读(63)  评论(0编辑  收藏  举报