[GDOI2017] 取石子游戏(LCA)

[GDOI2017] 取石子游戏(LCA)

题面

给出一棵树,每个点都有一个权值。对于每个节点,求去掉该节点的子树后,剩下所有节点的权值MEX(最小的没有出现的非负整数。)

分析

用权值线段树合并乱搞显然是可行的,但细节很多且需要卡常。

我们考虑所有权值为\(i\)的节点对答案的影响。求所有节点的LCA,那么对于从LCA向上到根的路径上的节点,去掉子树后的部分中一定没有值\(i\).那么值\(i\)就可能成为它们的MEX。

因此按权值从小到大,每次求出权值为\(i\)的节点的LCA,然后暴力往上跳,把没有被更新过的节点的答案设为\(i\),否则已经更新过的节点答案一定比\(i\)小,可以停止。当找到第一个没有节点出现的权值时,这个权值可以用来更新整棵树的MEX,直接扫一遍更新答案即可。

容易发现更新答案的复杂度是\(O(n)\)的,再加上求LCA,复杂度\(O(n \log n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define maxn 1000000
using namespace std;
template<typename T> void qread(T &x) {
	x=0;
	T sign=1;
	char c=getchar();
	while(c<‘0‘||c>‘9‘) {
		if(c==‘-‘) sign=-1;
		c=getchar();
	}
	while(c>=‘0‘&&c<=‘9‘) {
		x=x*10+c-‘0‘;
		c=getchar();
	}
	x=x*sign;
}
template<typename T> void qprint(T x) {
	if(x<0) {
		putchar(‘-‘);
		qprint(-x);
	} else if(x==0) {
		putchar(‘0‘);
		return;
	} else {
		if(x>=10) qprint(x/10);
		putchar(‘0‘+x%10);
	}
}

int cas,n,m;
struct edge {
	int from;
	int to;
	int next;
} E[maxn*2+5];
int esz=1;
int head[maxn+5];
void add_edge(int u,int v) {
	esz++;
	E[esz].from=u;
	E[esz].to=v;
	E[esz].next=head[u];
	head[u]=esz;
}
int a[maxn+5];

vector<int>pos[maxn+5];
int ans[maxn+5];
int fa[maxn+5],son[maxn+5],sz[maxn+5],top[maxn+5],deep[maxn+5];
void dfs1(int x,int f) {
	fa[x]=f;
	sz[x]=1;
	son[x]=0;
	deep[x]=deep[f]+1;
	for(int i=head[x]; i; i=E[i].next) {
		int y=E[i].to;
		if(y!=f) {
			dfs1(y,x);
			sz[x]+=sz[y];
			if(sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
void dfs2(int x,int t) {
	top[x]=t;
	if(son[x]) dfs2(son[x],t);
	for(int i=head[x]; i; i=E[i].next) {
		int y=E[i].to;
		if(y!=fa[x]&&y!=son[x]) {
			dfs2(y,y);
		}
	}
}
int lca(int x,int y) {
	while(top[x]!=top[y]) {
		if(deep[top[x]]<deep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	if(deep[x]<deep[y]) return x;
	else return y;
}

void clear() {
	memset(E,0,sizeof(E));
	esz=1;
	memset(head,0,sizeof(head));
	memset(a,0,sizeof(a));
	memset(fa,0,sizeof(fa));
	memset(son,0,sizeof(son));
	memset(sz,0,sizeof(sz));
	memset(top,0,sizeof(top));
	memset(deep,0,sizeof(deep));
}
int main() {
#ifdef LOCAL
	freopen("game9.in","r",stdin);
	freopen("game9.ans","w",stdout);
#endif
	int u,v;
	qread(cas);
	while(cas--) {
		clear();
		qread(n);
		qread(m);
		for(int i=1; i<=n; i++) qread(a[i]);
		cerr<<"ok"<<endl;
		for(int i=1; i<=m; i++) {
			qread(u);
			qread(v);
			add_edge(u,v);
			add_edge(v,u);
		}
		cerr<<"ok"<<endl;
		for(int i=0; i<=n; i++) pos[i].clear();
		for(int i=1; i<=n; i++) ans[i]=-1;
		for(int i=1; i<=n; i++) pos[a[i]].push_back(i);
		cerr<<"ok"<<endl;
		dfs1(1,0);
		dfs2(1,1);
		cerr<<"ok"<<endl;
		for(int i=0; i<=n; i++) {
			if(pos[i].empty()) {
				for(int j=1; j<=n; j++) if(ans[j]==-1) ans[j]=i;
				break;//比i更大的无意义
			}
			int lc=0;
			for(int j=0; j<(int)pos[i].size(); j++) {
				if(lc==0) lc=pos[i][j];
				else lc=lca(lc,pos[i][j]);
			}
			//除了lc子树外,剩下的部分都没有值i
			for(int x=lc; x&&ans[x]==-1; x=fa[x]) ans[x]=i; //更新mex
		}
		for(int i=1; i<=n; i++) {
			if(ans[i]==-1) ans[i]=0;
			qprint(ans[i]);
			putchar(‘ ‘);
		}
		putchar(‘\n‘);
	}
//	system("pause");
}
/*
1
6 5
5 2 1 0 3 1
1 2
1 3
1 4
3 5
2 6

*/

原文地址:https://www.cnblogs.com/birchtree/p/12547489.html

时间: 2024-09-30 11:15:45

[GDOI2017] 取石子游戏(LCA)的相关文章

poj 1067||hdu 1527 取石子游戏(博弈论,Wythoff Game)

取石子游戏 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 37893   Accepted: 12684 Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的石子.最后把石子全部取完者为胜者.现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者

HDU 1527 取石子游戏 威佐夫博弈

题目来源:HDU 1527 取石子游戏 题意:中文 思路:威佐夫博弈 必败态为 (a,b ) ai + i = bi     ai = i*(1+sqrt(5.0)+1)/2   这题就求出i然后带人i和i+1判断是否成立 以下转自网上某总结 有公式ak =[k(1+√5)/2],bk= ak + k  (k=0,1,2,-,n 方括号表示取整函数) 其中出现了黄金分割数(1+√5)/2 = 1.618-,因此,由ak,bk组成的矩形近似为黄金矩形 由于2/(1+√5)=(√5-1)/2,可以先

BZOJ 1874: [BeiJing2009 WinterCamp]取石子游戏 [Nim游戏 SG函数]

小H和小Z正在玩一个取石子游戏. 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏. 小H先进行操作,他想问你他是否有必胜策略,如果有,第一步如何取石子. N≤10 Ai≤1000 裸SG函数啊 然而我连SG函数都不会求了,WA了一会儿之后照别人代码改发现vis公用了... #include <iostream> #include <cstdio> #include <cstring> #includ

hdu 2516 取石子游戏

取石子游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2816    Accepted Submission(s): 1626 Problem Description 1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍.取完者胜.先取者负输出"Second win&qu

BZOJ 1413 取石子游戏(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1413 题意:n堆石子排成一排.每次只能在两侧的两堆中选择一堆拿.至少拿一个.谁不能操作谁输. 思路:参考这里. int f1[N][N],f2[N][N],n,a[N]; void deal() { RD(n); int i,j,k; FOR1(i,n) RD(a[i]),f1[i][i]=f2[i][i]=a[i]; int p,q,x; for(k=2;k<=n;k++) for(

POJ - 1067 取石子游戏(包括贝蒂定理的巧妙证明)

关键词: 取石子游戏.威佐夫博奕.betty贝蒂定理.胜态.负态.状态转移.覆盖(分划).高斯函数.第二数学归纳法.黄金分割比例 题目: Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的石子.最后把石子全部取完者为胜者.现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者. Input 输入包含若干行,表示若干种石

HDU 2516 取石子游戏 (博弈论)

取石子游戏 Problem Description 1堆石子有n个,两人轮流取.先取者第1次能够取随意多个,但不能所有取完.以后每次取的石子数不能超过上次取子数的2倍.取完者胜.先取者负输出"Second win".先取者胜输出"First win". Input 输入有多组.每组第1行是2<=n<2^31. n=0退出. Output 先取者负输出"Second win". 先取者胜输出"First win".

hdu 1527 取石子游戏(威佐夫博奕模板题)

取石子游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3562    Accepted Submission(s): 1789 Problem Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的

取石子游戏(hdu1527+威佐夫博弈)

S - 取石子游戏 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 1527 Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的石子.最后把石子全部取完者为胜者.现在给出初始的两堆石子的数目,如果轮到