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之后该区间原先待 ...
随机推荐
- B+树在NTFS文件系统中的应用
B+树在NTFS文件系统中的应用 flyfish 2015-7-6 卷(volume) NTFS的结构首先从卷開始. 卷相应于磁盘上的一个逻辑分区,当你将一个磁盘或者磁盘的一部分格式化成NTFS,卷将 ...
- 【知识梳理1】Android触摸事件机制
前言 随着科学技术的发展,智能手机早已成为我们当代人身边不可缺少的"伙伴"之中的一个,堪比对象女友.每天我们对着手机反复的做着点击.滑动操作,而手机则随着我们的操作给我们展示她的精 ...
- xgboost python windows编译问题
1.作为kaggle上非常火的机器学习包xgboost,windows python包安装起来真的非常麻烦,安装了一整天才成功. 2.请先下载xgboost-master,csdn上有资源的,能够在这 ...
- Orange's_1_win7下搭建环境
工欲善其事,必先利其器. 由于公司电脑工作环境是win7,为了学习于渊的Orange,所以就在windows下配置环境: 1.nasm: nasm汇编 http://www.nasm.us/ ...
- ASP.NET MVC中为DropDownListFor设置选中项的方法
在MVC中,当涉及到强类型编辑页,如果有select元素,需要根据当前Model的某个属性值,让Select的某项选中.本篇只整理思路,不涉及完整代码. □ 思路 往前台视图传的类型是List< ...
- C项目实践--图书管理系统(4)
前面已经把图书管理系统的所有功能模块都已实现完毕了,下面通过运行来分析该系统的操作流程并检验是否符合逻辑设计要求. 3.系统操作过程 F5 运行 1.登录系统 系统运行之后,提示输入用户名和密码,系统 ...
- 2.7 xargs和exec详解【转】
本文转载自:http://ask.apelearn.com/question/13323 常用在查找中exec主要是和find一起配合使用,而xargs就要比exec用的地方要多了. exec 应用 ...
- 软件开发-MSF方法(《构建之法》读书笔记2)
MSF-微软解决方案框架,是一套大型系统开发指南,它描述了如何用组队模型.过程模型和应用模型来开发Client/Server结构的应用程序,是在微软的工具和技术的基础上建立并开发分布式企业系统应用的参 ...
- Mybatis用到的设计模式和常用类原理
功能:将java对象映射城sql语句,将结果集转化成java对象.将容易变化的放在配置文件中,不变的通过Mybatis管理. 完成:1.根据JDBC规范建立数据库的连接: 2.通过反射打通java对象 ...
- bzoj4031
4031: [HEOI2015]小Z的房间 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 823 Solved: 407[Submit][Statu ...