hdu5002:Tree (LCT)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5002

题目大意:一开始给你一棵有N个点的树(N<=10^5),每个点有权值。现在有四种操作:1:x y a b删掉连接x,y的边,并连接a,b,保证操作完后还是一棵树;2:a b x 将a到b路径上的点权值全部变为x;3:a b d 将a到b路径上的点权值全部+d;4:a b 询问a到b中路径上第二大的数是多少(注意,是严格的第二大),以及有多少个。如果没有第二大的数,输出“ALL SAME”。操作个数M<=10^5,一个测试点有不超过3组数据。

题目分析:编程一中午,码出动态树。——第一道LCT

这题算是LCT的半裸题吧。前面的操作都很简单,但是我们如何知道一条链上第二大的数有几个呢?我想先求出第二大的数是几,然后搞一棵可持久化线段树之类乱七八糟脑洞大开地维护每一个数在每一条链上出现几次,然后死命想不出来。直到tututu一语惊醒梦中人:在splay的节点处维护一个最大值,以及最大的个数,还有次大值和次大值的个数不就好了吗?合并的时候讨论一下。我这才发现我好像做数据结构题已经做傻了,把问题复杂化QAQ……

然后码了一个中午,最后发现WA的原因竟然是忘了开long long……

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=100100;
const long long oo=11000000000000001LL;
const long long low=-11000000000001LL;
typedef long long LL;

struct Tnode;
void Update(Tnode *,Tnode *);
void Add(LL ,Tnode *);
void Change(LL ,Tnode *);

struct Tnode
{
	LL val,max1,max2,change,add;
	int Size,num1,num2,path_parent;
	bool flip;
	Tnode *fa,*son[2];
	int Get_d() { return fa->son[1]==this; }
	void Connect(Tnode *P,int d) { (son[d]=P)->fa=this; }
	void Up()
	{
		Size=(son[0]? son[0]->Size:0)+(son[1]? son[1]->Size:0)+1;
		max1=val,num1=1;
		max2=-oo,num2=0;
		if (son[0]) Update(this,son[0]);
		if (son[1]) Update(this,son[1]);
	}
	void Push_down()
	{
		if (flip)
		{
			swap(son[0],son[1]);
			if (son[0]) son[0]->flip^=1;
			if (son[1]) son[1]->flip^=1;
			flip=false;
		}
		if (add)
		{
			if (son[0]) Add(add,son[0]);
			if (son[1]) Add(add,son[1]);
			add=0;
		}
		if (change<oo)
		{
			if (son[0]) Change(change,son[0]);
			if (son[1]) Change(change,son[1]);
			change=oo;
		}
	}
} tree[maxn];

void Work(Tnode *x,LL Max,int Num)
{
	if (Max>x->max1) x->max2=x->max1,x->num2=x->num1,x->max1=Max,x->num1=Num;
	else
		if (Max==x->max1) x->num1+=Num;
		else
			if (Max>x->max2) x->max2=Max,x->num2=Num;
			else if (Max==x->max2) x->num2+=Num;
}

void Update(Tnode *x,Tnode *y)
{
	Work(x,y->max1,y->num1);
	Work(x,y->max2,y->num2);
}

void Add(LL a,Tnode *y)
{
	if (y->change<oo) y->change+=a;
	else y->add+=a;
	y->val+=a;
	y->max1+=a;
	y->max2+=a;
}

void Change(LL c,Tnode *y)
{
	y->val=y->max1=y->change=c;
	y->add=0LL;
	y->num1=y->Size;
	y->max2=-oo;
	y->num2=0;
}

Tnode *Node[maxn];
int cur;

int t,n,m;

Tnode *New_node(LL v)
{
	cur++;
	tree[cur].val=tree[cur].max1=v;
	tree[cur].Size=tree[cur].num1=1;
	tree[cur].max2=-oo;
	tree[cur].num2=0;
	tree[cur].change=oo;
	tree[cur].add=tree[cur].path_parent=0;
	tree[cur].flip=false;
	tree[cur].fa=tree[cur].son[0]=tree[cur].son[1]=NULL;
	return tree+cur;
}

void Push(Tnode *P)
{
	if (!P) return;
	Push(P->fa);
	P->Push_down();
}

void Zig(Tnode *P)
{
	int d=P->Get_d();
	Tnode *F=P->fa;
	if (P->son[!d]) F->Connect(P->son[!d],d);
	else F->son[d]=NULL;
	if (F->fa) F->fa->Connect(P,F->Get_d());
	else P->fa=NULL;
	F->Up();
	P->Connect(F,!d);
	P->path_parent=F->path_parent;
	F->path_parent=0;
}

void Splay(Tnode *P)
{
	Push(P);
	while (P->fa)
	{
		Tnode *F=P->fa;
		if (F->fa) ( F->Get_d()^P->Get_d() )? Zig(P):Zig(F);
		Zig(P);
	}
	P->Up();
}

void Down(int x)
{
	Splay(Node[x]);
	if (Node[x]->son[1]) Node[x]->son[1]->fa=NULL,Node[x]->son[1]->path_parent=x;
	Node[x]->son[1]=NULL;
	Node[x]->Up();
}

void Access(int x)
{
	Down(x);
	int y=Node[x]->path_parent;
	while (y)
	{
		Down(y);
		Node[y]->Connect(Node[x],1);
		Node[y]->Up();
		Node[x]->path_parent=0;
		x=y;
		y=Node[x]->path_parent;
	}
}

void Evert(int x)
{
	Access(x);
	Splay(Node[x]);
	Node[x]->flip^=1;
}

void Link(int x,int y)
{
	Evert(y);
	Splay(Node[y]);
	Node[y]->path_parent=x;
}

void Cut(int x,int y)
{
	Evert(x);
	Access(y);
	Splay(Node[x]);
	Node[x]->son[1]->fa=NULL,Node[x]->son[1]->path_parent=0;
	Node[x]->son[1]=NULL;
	Node[x]->Up();
}

int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	
	scanf("%d",&t);
	for (int g=1; g<=t; g++)
	{
		printf("Case #%d:\n",g);
		scanf("%d%d",&n,&m);
		cur=-1;
		for (int i=1; i<=n; i++)
		{
			int v;
			scanf("%d",&v);
			Node[i]=New_node(v);
		}
		
		for (int i=1; i<n; i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			Link(u,v);
		}
		
		for (int i=1; i<=m; i++)
		{
			int c;
			scanf("%d",&c);
			if (c==1)
			{
				int x,y,a,b;
				scanf("%d%d%d%d",&x,&y,&a,&b);
				Cut(x,y);
				Link(a,b);
			}
			
			if (c==2)
			{
				int a,b,x;
				scanf("%d%d%d",&a,&b,&x);
				Evert(a);
				Access(b);
				Splay(Node[a]);
				Change(x,Node[a]);
			}
			
			if (c==3)
			{
				int a,b,d;
				scanf("%d%d%d",&a,&b,&d);
				Evert(a);
				Access(b);
				Splay(Node[a]);
				Add(d,Node[a]);
			}
			
			if (c==4)
			{
				int a,b;
				scanf("%d%d",&a,&b);
				Evert(a);
				Access(b);
				Splay(Node[a]);
				
				if (Node[a]->max2<=low) printf("ALL SAME\n");
				else printf("%I64d %d\n",Node[a]->max2,Node[a]->num2);
			}
		}
	}
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值