bzoj2333[SCOI2011]棘手的操作 洛谷P3273 [SCOI2011]棘手的操作
2333?
先记一下吧,这题现在全部都是照着题解做的,因为怎么改都改不出来,只好对着题解改,以后还要再做过
以后再也不用指针了!太恶心了!空指针可不止直接特判那么简单啊,竟然还要因为空指针写奇怪的分类讨论!
没错,就是那个诡异的55和63行。由于要返回删除x后x所在树的新根,要分类讨论:如果x是根且其两个子节点合并后为空,那么去掉x后新树树根为空;如果x是根且其两个子节点合并后不为空,那么去掉x后新树树根为两个子节点合并后的;如果x不是根,那么去掉x后新树树根为原来的find(x)。
另外,打了注释号的(不管是空注释还是被注释掉的语句)都表示此处出过错
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
multiset<int> sx;
void erase(int x)
{
//if(sx.find(x)!=sx.end())
sx.erase(sx.find(x));
}
struct Node
{
int data;int addv;
Node *ch[],*fa;
void pd()
{
if(addv)
{
if(ch[]) ch[]->addv+=addv,ch[]->data+=addv;
if(ch[]) ch[]->addv+=addv,ch[]->data+=addv;
addv=;//
}
}
}nodes[];
Node* find(Node* x)//
{
if(x==NULL) return x;//
while(x->fa) x=x->fa;
return x;
}
Node* merge(Node* a,Node* b)
{
if(!a) return b;
if(!b) return a;
//a->pd();b->pd();
if(a->data < b->data) swap(a,b);
a->pd();
a->ch[]=merge(a->ch[],b);
if(a->ch[])/**/ a->ch[]->fa=a;
swap(a->ch[],a->ch[]);
return a;
}
Node *q[];int q_num;
void solvetag(Node *x)//
{
while(x)q[++q_num]=x,x=x->fa;
//while(x->fa)q[++q_num]=x,x=x->fa;
while(q_num)q[q_num--]->pd();
}
Node* del(Node *x)//删除x,将x较大的儿子提上来,并返回它所在集合的新根节点
{
//if(x==NULL) return x;//''
solvetag(x);
Node *t=merge(x->ch[],x->ch[]),*f=x->fa,*rt;
rt=find(x);if(rt==x) rt=NULL;//
x->ch[]=x->ch[]=x->fa=NULL;
if(f)//
{
if(x==f->ch[]) f->ch[]=t;
else f->ch[]=t;
}
if(t)/**/ t->fa=f;
if(t) rt=find(t);//
return rt;
}
void add(Node *x,int val)
{
//if(x==NULL) return;
solvetag(x);
erase(find(x)->data);
//find(x)->data+=val;
x->data+=val;Node *t=del(x);
sx.insert(merge(x,t)->data);//puts("t1");
}
void hadd(Node *x,int val)
{
//if(x==NULL) return;//'
Node *p=find(x);
erase(p->data);
p->addv+=val;p->data+=val;
sx.insert(p->data);
}
int n,m,addx,Q;
char tmp[];
int main()
{
int i,t,x,y,v;Node *fx,*fy;
scanf("%d",&n);
for(i=;i<=n;i++)
{
scanf("%d",&t);
nodes[i].data=t;
//nodes[i].upd();
sx.insert(t);
}
scanf("%d",&Q);
//int axx=0;
while(Q--)
{
scanf("%s",tmp);
if(tmp[]=='U')
{
scanf("%d%d",&x,&y);
fx=find(nodes+x);fy=find(nodes+y);
if(fx==fy) continue;
//solvetag(nodes+x);solvetag(nodes+y);
if(merge(fx,fy)==fx) erase(fy->data);
else erase(fx->data);
}
else if(tmp[]=='A')
{
if(tmp[]=='')
{
scanf("%d%d",&x,&v);
add(nodes+x,v); }
else if(tmp[]=='')
{
scanf("%d%d",&x,&v);
hadd(nodes+x,v);
}
else if(tmp[]=='')
{
scanf("%d",&v);
addx+=v;
}
}
else if(tmp[]=='F')
{
//axx++;
//if(axx==54) printf(" %c ",tmp[1]);
if(tmp[]=='')
{
scanf("%d",&x);
solvetag(nodes+x);
printf("%d\n",nodes[x].data+addx);
}
else if(tmp[]=='')
{
scanf("%d",&x);
//solvetag(nodes+x);
printf("%d\n",find(nodes+x)->data+addx);
}
else if(tmp[]=='')
{
printf("%d\n",*(--sx.end())+addx);
} }
}
return ;
}
另外:本来是想不到去写斜堆的,因为斜堆并不保证任何意义上的平衡,如果用这个在节点上维护附加信息,那么标记传递还有找根什么的复杂度应该是假的。然而实际应用发现...并没有问题?仍然不会分析
另外:把堆合并改成非旋treap里面的样子,也可以过,而且用时几乎完全一样(只差几ms)(然而去掉swap两个子节点就不能了,或者将非旋treap合并里面一些对正确性无关紧要的东西乱改一下也不行,这次跟洛谷上某道可并堆模版不一样了...),说明非旋treap里面堆合并跟一般斜堆合并写法本质应该没有区别(只是保证中序遍历不变)。然而并不会分析?
另外:虽然不交换是错的,但是随机怎么摆却是对的!实际效果是只比斜堆慢了一点点。而且,即使斜堆真的会被卡随机堆也不可能被卡...应该吧
就比如这样
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
multiset<int> sx;
inline int rd()
{
static int x=;
return x=(48271LL*x+)%;
}
void erase(int x)
{
//if(sx.find(x)!=sx.end())
sx.erase(sx.find(x));
}
struct Node
{
int data;int addv;
Node *ch[],*fa;
void pd()
{
if(addv)
{
if(ch[]) ch[]->addv+=addv,ch[]->data+=addv;
if(ch[]) ch[]->addv+=addv,ch[]->data+=addv;
addv=;//
}
}
}nodes[];
Node* find(Node* x)//
{
if(x==NULL) return x;//
while(x->fa) x=x->fa;
return x;
}
Node* merge(Node* a,Node* b)
{
if(!a) return b;
if(!b) return a;
//a->pd();b->pd();
if(a->data < b->data) swap(a,b);
a->pd();
if(rd()%)
{
a->ch[]=merge(a->ch[],b);
if(a->ch[]) a->ch[]->fa=a;
}
else
{
a->ch[]=merge(a->ch[],b);
if(a->ch[]) a->ch[]->fa=a;
}
return a; }
Node *q[];int q_num;
void solvetag(Node *x)//
{
while(x)q[++q_num]=x,x=x->fa;
//while(x->fa)q[++q_num]=x,x=x->fa;
while(q_num)q[q_num--]->pd();
}
Node* del(Node *x)//删除x,将x较大的儿子提上来,并返回它所在集合的新根节点
{
//if(x==NULL) return x;//''
solvetag(x);
Node *t=merge(x->ch[],x->ch[]),*f=x->fa,*rt;
rt=find(x);if(rt==x) rt=NULL;//
x->ch[]=x->ch[]=x->fa=NULL;
if(f)//
{
if(x==f->ch[]) f->ch[]=t;
else f->ch[]=t;
}
if(t)/**/ t->fa=f;
if(t) rt=find(t);//
return rt;
}
void add(Node *x,int val)
{
//if(x==NULL) return;
solvetag(x);
erase(find(x)->data);
//find(x)->data+=val;
x->data+=val;Node *t=del(x);
sx.insert(merge(x,t)->data);//puts("t1");
}
void hadd(Node *x,int val)
{
//if(x==NULL) return;//'
Node *p=find(x);
erase(p->data);
p->addv+=val;p->data+=val;
sx.insert(p->data);
}
int n,m,addx,Q;
char tmp[];
int main()
{
int i,t,x,y,v;Node *fx,*fy;
scanf("%d",&n);
for(i=;i<=n;i++)
{
scanf("%d",&t);
nodes[i].data=t;
//nodes[i].upd();
sx.insert(t);
}
scanf("%d",&Q);
//int axx=0;
while(Q--)
{
scanf("%s",tmp);
if(tmp[]=='U')
{
scanf("%d%d",&x,&y);
fx=find(nodes+x);fy=find(nodes+y);
if(fx==fy) continue;
//solvetag(nodes+x);solvetag(nodes+y);
if(merge(fx,fy)==fx) erase(fy->data);
else erase(fx->data);
}
else if(tmp[]=='A')
{
if(tmp[]=='')
{
scanf("%d%d",&x,&v);
add(nodes+x,v); }
else if(tmp[]=='')
{
scanf("%d%d",&x,&v);
hadd(nodes+x,v);
}
else if(tmp[]=='')
{
scanf("%d",&v);
addx+=v;
}
}
else if(tmp[]=='F')
{
//axx++;
//if(axx==54) printf(" %c ",tmp[1]);
if(tmp[]=='')
{
scanf("%d",&x);
solvetag(nodes+x);
printf("%d\n",nodes[x].data+addx);
}
else if(tmp[]=='')
{
scanf("%d",&x);
//solvetag(nodes+x);
printf("%d\n",find(nodes+x)->data+addx);
}
else if(tmp[]=='')
{
printf("%d\n",*(--sx.end())+addx);
} }
}
return ;
}
我差点忘了一件事,就是相同权值的在堆里顺序无所谓,merge两个参数先后也是无所谓的。
bzoj2333[SCOI2011]棘手的操作 洛谷P3273 [SCOI2011]棘手的操作的更多相关文章
- 洛谷P3273 [SCOI2011] 棘手的操作 [左偏树]
题目传送门 棘手的操作 题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 ...
- 洛谷P3273 [SCOI2011]棘手的操作
题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作:U x y: 加一条边,连接第x个节点和第y个节点A1 x v: 将第x个节点的权 ...
- BZOJ2330或洛谷3275 [SCOI2011]糖果
BZOJ原题链接 洛谷原题链接 很明显的差分约束,但数据范围较大,朴素\(SPFA\)判正环求解会\(T\)(理论上如此,但我看到有挺多人用朴素的还跑得挺快..),所以需要优化. 我们所建立的有向图中 ...
- 洛谷P3275 [SCOI2011]糖果(差分约束,最长路,Tarjan,拓扑排序)
洛谷题目传送门 差分约束模板题,等于双向连0边,小于等于单向连0边,小于单向连1边,我太蒻了,总喜欢正边权跑最长路...... 看遍了讨论版,我是真的不敢再入复杂度有点超级伪的SPFA的坑了 为了保证 ...
- AC日记——[HAOI2015]树上操作 洛谷 P3178
题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a .操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 ...
- bzoj2333 [SCOI2011]棘手的操作(洛谷3273)
题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作:U x y: 加一条边,连接第x个节点和第y个节点A1 x v: 将第x个节点的权 ...
- 洛谷.3273.[SCOI2011]棘手的操作(左偏树)
题目链接 还是80分,不是很懂. /* 七个操作(用左偏树)(t2表示第二棵子树): 1.合并:直接合并(需要将一个t2中原有的根节点删掉) 2.单点加:把这个点从它的堆里删了,加了再插入回去(有负数 ...
- 【POJ 3159】Candies&&洛谷P3275 [SCOI2011]糖果
来补一下自己很久以前那个很蒟蒻很蒟蒻的自己没有学懂的知识 差分约束,说白了就是利用我们在求最短路的一个\(relax\)操作时的判断的原理 \[dis[v]>dis[u]+disj(u,v)\] ...
- bzoj 1858: [Scoi2010]序列操作 || 洛谷 P2572
记一下:线段树占空间是$2^{ceil(log2(n))+1}$ 这个就是一个线段树区间操作题,各种标记的设置.转移都很明确,只要熟悉这类题应该说是没有什么难度的. 由于对某区间set之后该区间原先待 ...
随机推荐
- ThoughtWorks技术雷达
ThoughtWorks技术雷达 技术成熟方案的一个推荐网站.
- Android中View窗口getWidth和getMeasuredWidth的差别
今天在研究自己定义listview的下拉刷新的效果.想移植到项目需求中,再看自己定义源代码时发现了一个问题就是getWidth和getMeasuredWidth两个方法有什么差别,求教万能的百度,经调 ...
- sshclientCRT连接linux使用技巧
设置仿真和回滚缓冲区 字体外观设置 日志文件设置 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fi ...
- 邮件:事务失败。 服务器响应为:DT:SPM 163 smtp
几年前我做的一个项目,日发邮件最高峰时几十万.自以为对邮件发送方面已经有了一定认识,所以近期机缘巧合之下,又有项目需要发送邮件,不禁自信满满,暗自庆幸能不手到擒来乎? 不想老革命遇到新问题.我原先的邮 ...
- 2016/2/25 1、<表单验证<form></form> 2、正则表达式 3、事件
1.<表单验证<form></form> (1).非空验证(去空格) (2).对比验证(跟一个值对比) (3).范围验证(根据一个范围进行判断) (4).固定格式验证:电 ...
- 请说出作用域public,private,protected,以及不写时的区别
这四个作用域的可见范围如下表所示. 说明:如果在修饰的元素上面没有写任何访问修饰符,则表示friendly. 作用域 当前类 同一package 子孙类 其他package public ...
- [Unity3D]Unity3D游戏开发之连续滚动背景
在诸如天天跑酷等2D游戏中.因为游戏须要表现出运动的感觉.通常都会使游戏背景连续循环滚动以增强视觉效果,那么今天.博主就来带领大家一起来实现连续滚动背景吧. 首先来讲述一下原理.准备两张连续的图片(博 ...
- 搭建nodejs服务,访问本地站点文件
搭建nodejs服务器步骤: 1.安装nodejs服务(从官网下载安装) 2.在自己定义的目录下新建服务器文件如 server.js 例如,我在E:\PhpProject\html5\websocke ...
- easyUI下拉列表点击事件的使用
可以通过input 和select来创建下拉列表 其中select的创建如下: 通过json来创建js数组 [{ "id":1, "text":"te ...
- WebService_使用三要素
一.Java中WebService规范 JAVA 中共有三种WebService 规范,分别是JAX-WS.JAX-RS.JAXM&SAAJ(废弃). 1.JAX-WS规范 JAX-WS 的全 ...