POJ 3481 Double Queue
每个顾客有 编号1e6,优先级1e7
银行可以先服务 当前队列中优先级最大(2)或者最小(3)的顾客, 输出其编号
我们可以按 优先级 来建树的相对顺序
然后对于 优先级为x 的顾客我们 在树中寻找他是第几个 (k),然后进行操作。
const int INF=0x3f3f3f3f;
#define N 200005
#define key_value ch[ch[root][1]][0] //经常利用这个
int pre[N],ch[N][2],key[N],size[N];
int sx[N];
int root,tot;
int a[N];
void newnode(int &r,int father,int p,int k){ //注意r 需要引用
r=++tot; //可知我们的根节点从1开始
pre[r]=father; //更新 pre
size[r]=1; //新开节点只有自己
ch[r][0]=ch[r][1]=0;
sx[r]=p;
key[r]=k;
}
void push_up(int r){ //向上更新
size[r]=size[ch[r][0]] + size[ch[r][1]] +1;
}
void Rotate(int x,int kind)//对X旋转,0为左旋,1为右旋 该部分基本固定
{
int y=pre[x];
ch[y][!kind]=ch[x][kind];
pre[ch[x][kind]]=y;
if(pre[y])
ch[pre[y]][ch[pre[y]][1]==y]=x;
pre[x]=pre[y];
ch[x][kind]=y;
pre[y]=x;
push_up(y);
}
void Splay(int r,int goal) ////Splay调整,将结点r调整到goal下面
{
while(pre[r]!=goal)
{
if(pre[pre[r]]==goal)
{
//这题有反转操作,需要先push_down,在判断左右孩子
Rotate(r,ch[pre[r]][0]==r);
}
else
{
//这题有反转操作,需要先push_down,在判断左右孩子
int y=pre[r];
int kind=(ch[pre[y]][0]==y);
//两个方向不同,则先左旋再右旋
if(ch[y][kind]==r)
{
Rotate(r,!kind);
Rotate(r,kind);
}
//两个方向相同,相同方向连续两次
else
{
Rotate(y,kind);
Rotate(r,kind);
}
}
}
push_up(r);
if(goal==0)root=r;
}
int Get_Min(int r) //找到最值
{
while(ch[r][0])
{
r=ch[r][0];
}
return r;
}
int Get_Max(int r)
{
while(ch[r][1])
{
r=ch[r][1];
}
return r;
}
int get_kth(int r,int k) //我们得到的是第k个节点的位置。但是我们注意init的时候添加两个极大极小点。
{
int t=size[ch[r][0]]+1;// 我们添加过一个很小的点。
if(t==k)return r;
if(t>k)return get_kth(ch[r][0],k);
else return get_kth(ch[r][1],k-t);
}
int get(int p,int now,int cnt){ // 优先级为p 在 树中是第几个
if(size[now]==0 ){ //题意没有相同的 p?
return cnt;
}
if(p<sx[now]){
get(p,ch[now][0],cnt);
}
else{
get(p,ch[now][1],cnt+1+size[ ch[now][0] ]);
}
}
//void insert(int x,int p,int k){ // 插入一个节点 到 第x个后面
// Splay(get_kth(root,x+1),0); // x转至根
// Splay(get_kth(root,x+2),root); //x+1转到root右儿子,此时x+2.... 都在右边
// newnode(key_value,ch[root][1],k,p);
// push_up(ch[root][1]);
// push_up(root);
//}
void insert(int p,int k){
int pos=get(p,root,0); // 我们应该放在 pos的后面,但包含最小点,所以应该是放在pos-1的后面
// printf("p= %d pos=%d\n",p,pos);
Splay(get_kth(root,pos),0); // pos-1
Splay(get_kth(root,pos+1),root); // pos
newnode(key_value,ch[root][1],p,k);
push_up(ch[root][1]);
push_up(root);
// printf("now now+1 %d %d\n",key[root],key[ch[root][1]]);
}
void del(int x){ // 删除第x个
Splay(get_kth(root,x),0); // x-1 移到0
Splay(get_kth(root,x+2),root); //x+1 移到x-1的右边 那么key_value=x
printf("%d\n",key[key_value] );
pre[key_value]=0;
key_value=0; //不知道算不算清除 //
push_up(ch[root][1]);
push_up(root);
}
void init(){
tot=root=0;
ch[root][0]=ch[root][1]=pre[root]=size[root]=0;
//root 地址为0,所以不需要赋m[root]的值
/*开一个理论上永远最大的根,和永远最小的根,这样操作有套路*/
newnode(root,0,-INF,0); // 这个点是永远最小的
newnode(ch[root][1],root,INF,0); //这个点永远最大
// sx[ch[root][1]]=INF;
push_up(ch[root][1]); //记得建完树之后更新到root
push_up(root);
}
int main()
{
// freopen("1.txt","r",stdin);
init();
int op=1;
int p,k;
while(1){
scanf("%d",&op);
if(op==1){
scanf("%d %d",&k,&p);
insert(p,k);
// printf("1111111 :%d\n",size[root]);
}
else if(op==2){ //最高 一共有size[root]-2个
if(size[root]==2){
printf("0\n");
continue;
}
del(size[root]-2);
}
else if(op==3){
if(size[root]==2){
printf("0\n");
continue;
}
del(1);
}
else
break;
}
return 0;
}
POJ 2828 Buy Tickets
Splay 竟然可耻的 TLE 了
我们用线段树可以比较容易的解决此问题, 解法有点类似逆序数。
HDU 1754 I Hate It
也是线段树啊。。。。别搞事啊。
HDU 3468 Treasure Hunting
这次更奇葩,这是个二分图匹配问题啊。。。。
HDU 1890 Robotic Sort
这个题很经典
using namespace std;
#define Key_value ch[ch[root][1]][0]
#define ll __int64
const int N=100005;
const int INF=0x3f3f3f3f;
int pre[N],key[N],ch[N][2],root,tot1;
//分别表示父结点,键值,左右孩子(0左1右),根结点,结点数量
int n;
ll sum[N]; // 字数的和
int size[N]; //子树节点数
int a[N];
int rev[N]; //lazy
struct node{
int v;
int i;
}f[N];
//新建一个结点
void NewNode(int &r,int father,int k) //新建节点 &的意义就是将编号(tot1)直接传给参数
{
r=k; // 二叉排序树在插入节点的时候就已经建好了,这个r这是一个节点编号
pre[r]=father;
size[r]=1; // size包括的自己
rev[r]=0;
ch[r][0]=ch[r][1]=0;//左右孩子为空
}
void push_rev(int r){
if(!r)
return;
swap(ch[r][0],ch[r][1]);
rev[r]^=1; //
}
void push_up(int r){ //更新 r的sum和size
size[r]=size[ch[r][0]] + size[ch[r][1]]+1;
}
void push_down(int r){
if(rev[r]){
push_rev(ch[r][0]); //直接交换,不再判断了
push_rev(ch[r][1]);
rev[r]=0;
}
}
//建树
//先建立中间结点,再两端的方法
void build(int &x,int l,int r,int father){ //这种建树方式 = 按位置大小 来建一颗二叉排序树
if(l>r) return;
int mid=(l+r)>>1;
NewNode(x,father,mid); //可以看出,就是拿原序列建树,并且树中节点编号就是原序列位置
build(ch[x][0],l,mid-1,x);
build(ch[x][1],mid+1,r,x);
push_up(x); //更新一下
}
void init(){
root=tot1=0;
ch[root][0]=ch[root][1]=pre[root]=size[root]=rev[root]=0;
NewNode(root,0,n+1); //先新建的头结点
NewNode(ch[root][1],root,n+2); //在头结点的右边再插入一个新节点,这个节点的位置十分精髓
//二叉排序树,只要插进去了,就永远决定的他们的大小关系,与key值就没有关系了,仔细想想不难发现
//这个节点,是所有节点最大的一个位置,因为整个序列都处在他的左子树中。
build(Key_value,1,n,ch[root][1]); // 从第二个节点开始建树
//记得维护下两个头结点
push_up(ch[root][1]);
push_up(root);
}
/*
1 2 3 4 5
0
|
3
/ \
1 5
/ \ / \
空 2 4 6
*/
//旋转,kind为1是右旋,为0是左旋
void Rotate(int x,int kind) //x 表示要旋转的节点编号
{
int y=pre[x];
push_down(y);
push_down(x);
ch[y][!kind]=ch[x][kind]; //父节点相反关系的子节点 = 节点的相同关系节点
pre[ch[x][kind]]=y; // 更新pre数组
if(pre[y]) // 如果Y存在(rotate到的位置不是根的话),要把y的儿子=x。
ch[pre[y]][ch[pre[y]][1]==y]=x;
pre[x]=pre[y];
ch[x][kind]=y;
pre[y]=x;
push_up(y);
}
//Splay调整,将结点r调整到goal下面
void Splay(int r,int goal)
{
push_down(r);
while(pre[r]!=goal)
{
if(pre[pre[r]]==goal){
//如果有反转操作,需要先push_down
push_down(pre[r]);
push_down(r);
Rotate(r,ch[pre[r]][0]==r);
}
else
{
push_down(pre[pre[r]]);
push_down(pre[r]);
push_down(r);
int y=pre[r];
int kind=ch[pre[y]][0]==y;
if(ch[y][kind]==r) //如果三点一线,则需要先 root 父亲结点
{
Rotate(r,!kind);
Rotate(r,kind);
}
else
{
Rotate(y,kind);
Rotate(r,kind);
}
}
}
push_up(r);
//更新根结点
if(goal==0)
root=r;
}
int get_kth(int r,int k){ //根据上面的建树方式可知是找序列中的第k个数(的编号)
push_down(r);
int t=size[ch[r][0]]+1;
if(t==k) return r;
if(t>k) return get_kth(ch[r][0],k);
else return get_kth(ch[r][1],k-t);
}
//找后继的节点编号
int get_next(int x)
{
push_down(x);
int tmp=ch[x][1]; //找右子树中最小的节点
if(0==tmp) return -1;
while(ch[tmp][0]){ //如果有反转 ,这里也要push 不然Tle
tmp=ch[tmp][0];
push_down(tmp);
}
return tmp;
}
bool cmp(node a,node b){
if(a.v==b.v) return a.i<b.i;
return a.v<b.v;
}
int main()
{
//freopen("1.txt","r",stdin);
while(~scanf("%d",&n) && n){
for(int i=1;i<=n;i++){
scanf("%d",&f[i].v);
f[i].i=i;
}
sort(f+1,f+n+1,cmp);
init();
for(int i=1;i<=n;i++){
//先输出第i大的数在什么地方
// f[i].v 即第i大的数字,在树中的位置f[i].i;
Splay(f[i].i,0); //用get_kth返回的是 树中的节点值
if(i==1) printf("%d",size[ch[root][0]]); //因为有一个初识节点在里面,所以不用+1
else printf(" %d",size[ch[root][0]]);
//然后反转区间[i,node[i].i]
Splay(get_kth(root,i),0); //先转 i-1
int k=get_next(f[i].i);
Splay(k,root);
push_rev(Key_value);
}
printf("\n");
}
return 0;
}
HDU 3436 Queue-jumpers
HDU 3487 Play with Chain
HYSBZ 1588 营业额统计
HYSBZ 1208 宠物收养所
HYSBZ 1267 Kth Number I
HYSBZ 1269 文本编辑器editor
HYSBZ 1500 维修数列
POJ 3580 SuperMemo
HDU 2475 Box
HDU 3726 Graph and Queries
POJ 3468 A Simple Problem with Integers