「Luogu P6157 有趣的游戏」

本篇题解不需要维护严格前四大值,也不需要会STL,适合各种语言的选手看思路.

题目大意

给出一颗有点权的无根树,支持单点修改(加上一个数),在树链上插叙两个点的点权(\(w_i,w_j\))使得 \(w_i \mod w_j\) 最大,且 \(w_i \not= w_j\),以及在去掉这两个点后的整颗树上查询两个点的点权(\(w_q,w_p\))使得 \(w_q \mod w_p\) 最大.

分析

考虑给出一些数其中任意一数给其他数取模后的最大值,可以发现这个最大值为这个数列的严格次大值 \(\%\) 最大值.那么就是要找出树链上的最大值和严格次大值,这显然可以用树链剖分线段树解决.

既然要用线段树维护,那么自然需要合并信息,而这里的最大值和严格次大值的合并比较麻烦.

先定义一个结构体:

struct FMSM
{
	int first_max,second_max;
	//表示最大值和严格次大值
};

下面是合并操作

FMSM MergeFMSM(FMSM a,FMSM b)//合并
{
	for_make.first_max=max(a.first_max,b.first_max);//合并以后的最大值一定等于合并的两个结构体中的最大值的最大值
	if(a.first_max!=b.first_max)//如果最大值不同时
	{
		for_make.second_max=min(a.first_max,b.first_max);//严格次大值为两最大值中小的那个
		for_make.second_max=max(for_make.second_max,max(a.second_max,b.second_max));//也可能为原来的严格次大值中大的那个
		return for_make;
	}
	for_make.second_max=max(a.second_max,b.second_max);//如果两个最大值相等那么严格次大值自然就是两严格次大值中大的那个
	return for_make;
}

然后就是要知道去掉两个数之后的严格次大了,这东西也很好办,可以用STL维护.

但是,我不会STL,所以可以发现严格次大就是最大的前驱(第一个小于它的数),因为需要删数&加数,那自然就会想到平衡树了,只要平衡树再维护一下就好了,非常简单.

代码

#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int MAXN=2e5+7;
const int INF=114514233;
int N,M;
struct Tree//平衡树部分,不多讲,可以用stl
{
	int lson,rson;
	int size;
	int val;
	int ind;
}tree[MAXN*2];
int fhq_cnt=0,root=0;
#define LSON tree[now].lson
#define RSON tree[now].rson
int New(int val)
{
	tree[++fhq_cnt].val=val;
	tree[fhq_cnt].ind=rand();
	tree[fhq_cnt].size=1;
	return fhq_cnt;
}
void FHQ_PushUp(int now)
{
	tree[now].size=tree[LSON].size+tree[RSON].size+1;
}
void Split(int now,int val,int &tree1,int &tree2)
{
	if(!now)
	{
		tree1=0;
		tree2=0;
		return;
	}
	if(tree[now].val<=val)
	{
		tree1=now;
		Split(RSON,val,RSON,tree2);
	}
	else
	{
		tree2=now;
		Split(LSON,val,tree1,LSON);
	}
	FHQ_PushUp(now);
}
int Merge(int tree1,int tree2)
{
	if(!tree1||!tree2)
	{
		return tree1+tree2;
	}
	if(tree[tree1].ind<tree[tree2].ind)
	{
		tree[tree1].rson=Merge(tree[tree1].rson,tree2);
		FHQ_PushUp(tree1);
		return tree1;
	}
	else
	{
		tree[tree2].lson=Merge(tree1,tree[tree2].lson);
		FHQ_PushUp(tree2);
		return tree2;
	}
}
int tree1,tree2,tree3;
void Insert(int val)
{
	Split(root,val,tree1,tree2);
	root=Merge(Merge(tree1,New(val)),tree2);
}
void Delete(int val)
{
	Split(root,val,tree1,tree3);
	Split(tree1,val-1,tree1,tree2);
	tree2=Merge(tree[tree2].lson,tree[tree2].rson);
	root=Merge(Merge(tree1,tree2),tree3);
}
int QueryKth(int now,int k)
{
	while(1)
	{
		if(tree[LSON].size+1==k)
		{
			return now;
		}
		if(tree[LSON].size>=k)
		{
			now=LSON;
		}
		else
		{
			k-=tree[LSON].size+1;
			now=RSON;
		}
	}
	return 0;
}
int QueryPre(int val)
{
	Split(root,val-1,tree1,tree2);
	int result=tree[QueryKth(tree1,tree[tree1].size)].val;
	root=Merge(tree1,tree2);
	return result;
}
#undef LSON
#undef RSON
int val[MAXN];
struct Edge//链式前向星
{
	int to,next;
}edge[MAXN*2];
int edge_head[MAXN];
int edge_cnt=0;
#define FOR(now) for(int edge_i=edge_head[now];edge_i;edge_i=edge[edge_i].next)
#define TO edge[edge_i].to
void AddEdge(int form,int to)
{
	edge[++edge_cnt].to=to;
	edge[edge_cnt].next=edge_head[form];
	edge_head[form]=edge_cnt;
}
//树剖部分
int point_deep[MAXN];
int point_father[MAXN];
int point_son[MAXN];
int point_size[MAXN];
void DFS_1(int now=1)
{
	int max_size=-1;
	point_size[now]=1;
	FOR(now)
	{
		if(point_father[now]!=TO)
		{
			point_father[TO]=now;
			point_deep[TO]=point_deep[now]+1;
			DFS_1(TO);
			point_size[now]+=point_size[TO];
			if(point_size[TO]>max_size)
			{
				max_size=point_size[TO];
				point_son[now]=TO;
			}
		}
	}
}
int chain_cnt=0;
int point_id[MAXN];
int point_val[MAXN];
int chain_top[MAXN];
void DFS_2(int now=1,int top=1)
{
	point_id[now]=++chain_cnt;
	point_val[chain_cnt]=val[now];
	chain_top[now]=top;
	if(!point_son[now])
	{
		return;
	}
	DFS_2(point_son[now],top);
	FOR(now)
	{
		if(TO!=point_father[now]&&TO!=point_son[now])
		{
			DFS_2(TO,TO);
		}
	}
}
#undef FOR
#undef TO
struct FMSM//最大和严格次大的结构体
{
	int first_max,second_max;
}for_make;
FMSM MergeFMSM(FMSM a,FMSM b)//合并
{
	for_make.first_max=max(a.first_max,b.first_max);
	if(a.first_max!=b.first_max)
	{
		for_make.second_max=min(a.first_max,b.first_max);
		for_make.second_max=max(for_make.second_max,max(a.second_max,b.second_max));
		return for_make;
	}
	for_make.second_max=max(a.second_max,b.second_max);
	return for_make;
}
FMSM MakeFMSM(int first_max,int second_max)
{
	for_make.first_max=first_max;
	for_make.second_max=second_max;
	return for_make;
}
FMSM fmsm_null=MakeFMSM(-INF,-INF);
struct SegmentTree//线段树部分
{
	FMSM max;
}sgt[MAXN*4];
#define LSON (now<<1)
#define RSON (now<<1|1)
#define MIDDLE ((left+right)>>1)
#define LEFT LSON,left,MIDDLE
#define RIGHT RSON,MIDDLE+1,right
#define NOW now_left,now_right
void SGT_PushUp(int now)
{
	sgt[now].max=MergeFMSM(sgt[LSON].max,sgt[RSON].max);
}
void Build(int now=1,int left=1,int right=N)
{
	if(left==right)
	{
		sgt[now].max=MakeFMSM(point_val[left],-INF);
		return;
	}
	Build(LEFT);
	Build(RIGHT);
	SGT_PushUp(now);
}
void Updata(int place,int w,int now=1,int left=1,int right=N)//单点修改
{
	if(place<left||right<place)
	{
		return;
	}
	if(left==right)
	{
		sgt[now].max=MakeFMSM(sgt[now].max.first_max+w,-INF);
		return;
	}
	Updata(place,w,LEFT);
	Updata(place,w,RIGHT);
	SGT_PushUp(now);
}
FMSM Query(int now_left,int now_right,int now=1,int left=1,int right=N)//区间查询
{
	if(now_right<left||right<now_left)
	{
		return fmsm_null;
	}
	if(now_left<=left&&right<=now_right)
	{
		return sgt[now].max;
	}
	return MergeFMSM(Query(NOW,LEFT),Query(NOW,RIGHT));//返回答案要合并
}
FMSM QueryMax(int u,int v)//插叙链
{
	FMSM result=fmsm_null;
	while(chain_top[u]!=chain_top[v])
	{
		if(point_deep[chain_top[u]]<point_deep[chain_top[v]])
		{
			swap(u,v);
		}
		result=MergeFMSM(result,Query(point_id[chain_top[u]],point_id[u]));
		u=point_father[chain_top[u]];
	}
	if(point_deep[u]>point_deep[v])
	{
		swap(u,v);
	}
	result=MergeFMSM(result,Query(point_id[u],point_id[v]));
	return result;
}
int main()
{
	scanf("%d",&N);
	int a,b;
	REP(i,1,N-1)
	{
		scanf("%d%d",&a,&b);
		AddEdge(a,b);
		AddEdge(b,a);
	}
	REP(i,1,N)
	{
		scanf("%d",&val[i]);
		Insert(val[i]);
	}
	DFS_1();
	DFS_2();
	Build();
	int opt,x,y;
	FMSM p;
	scanf("%d",&M);
	REP(i,1,M)
	{
		scanf("%d%d%d",&opt,&x,&y);
		if(opt==0)
		{
			Delete(val[x]);//删掉原来的数
			val[x]+=y;
			Insert(val[x]);//加入新数
			Updata(point_id[x],y);
		}
		if(opt==1)
		{
			p=QueryMax(x,y);
			if(p.second_max==-INF)//不存在不同数
			{
				printf("-1\n");
			}
			else
			{
				Delete(p.first_max);//先删掉
				Delete(p.second_max);
				a=QueryPre(QueryPre(INF));//最大值为极大值前驱,严格次大值为最大值前驱
				Insert(p.first_max);//重新加入
				Insert(p.second_max);
				printf("%d %d\n",p.second_max,a);//输出答案
			}
		}
	}
	return 0;
}//注意开O2
posted @ 2020-03-03 14:35  SxyLimit  阅读(189)  评论(0编辑  收藏  举报