Live2D

题解 Christmas Game

题目传送门

题目大意

给出 \(t\)\(n\) 个点 \(m\) 条边的无向图,每次可以从任意一棵树选择一条边删掉,然后该树不与根(为 \(1\) )联通的部分被删掉。不能操作的人输。问谁有必胜策略。

每棵树都满足:每个环都只会挂在叶子节点上。

\(n\le 100,m\le 500\)

思路

怎么说呢?很厉害的题目吧。

首先考虑一个树的情况,我们设 \(sg(u)\) 表示 \(u\) 子树内的 \(sg\) 函数值,我们可以得到转移式:

\[sg(u)=\text{mex}_{v\in son_u}\{sg(v)\} \]

\[\Rightarrow sg(u)=\otimes_{v\in son_u} (sg(v)+1) \]

这个可以通过打表发现,不过有一种比较巧妙的方法,就是说我们把主链拉出来,那么相当于每一个节点连了一条链,那么,删边就相当于取石子了。

然后考虑拓展到任意图上。这里给出一个结论:

一个环如果大小为偶数,它的顶点产生的贡献为 \(0\),反之为 \(1\)

相当于环大小为偶数时,把环缩为一个点,否则再连向一个点。

考虑证明,我们发现我们可以通过枚举破掉环上哪条边来求,你发现环大小为偶数时,你破掉之后两条链长度一定一奇一偶,也就是对该环的根(挂的叶子)产生的贡献一定为偶数,所以第一个未出现的正整数一定为 \(1\)。同理,我们可以推出环大小为奇数的情况,这里就不再赘述了。

这里提醒一些细节:

  • 需要考虑重边

  • 需要考虑多个环串在一个顶点的情况

具体见代码就好了。

\(\texttt{Code1}\)

#include <cstdio>
#include <vector>
#include <cstring>
#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 105

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int T,n,m,sg[MAXN],dep[MAXN],vis[MAXN];

int toop,head[MAXN],to[MAXN * 10],nxt[MAXN * 10];

void Add_Edge (int u,int v){
	to[++ toop] = v,nxt[toop] = head[u],head[u] = toop;
	to[++ toop] = u,nxt[toop] = head[v],head[v] = toop;
}

int dfs (int u,int fa){
	bool flag = 0;
	dep[u] = dep[fa] + 1,vis[u] = 1;
	for (Int i = head[u];i;i = nxt[i]){
		int v = to[i];
		if (!v) continue;
		if (v == fa && !flag){
			flag = 1;
			continue;
		}
		if (vis[v]){
			sg[v] ^= (dep[u] - dep[v] + 1 & 1);
			to[i ^ 1] = 0;
			return v;
		}
		else{
			int cur = dfs (v,u);
			if (!cur) sg[u] ^= sg[v] + 1;
			else if (cur ^ u) return cur;
		}
	}
	return 0;
}

void clear (){
	toop = 1,memset (head,0,sizeof (head));
	for (Int i = 1;i <= n;++ i) sg[i] = dep[i] = vis[i] = 0;
}

signed main(){
	while (~scanf ("%d",&T)){
		int ans = 0;
		while (T --> 0){
			read (n),read (m),clear ();
			for (Int i = 1,u,v;i <= m;++ i) read (u),read (v),Add_Edge (u,v);
			dfs (1,0),ans ^= sg[1];
		}
		puts (ans ? "Sally" : "Harry");
	} 
	return 0;
}

\(\texttt{Code2}\)

#include <cstdio>
#include <vector>
using namespace std;

#define Int register int
#define MAXN 105

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

vector <int> G[MAXN];
int T,n,m,top,sg[MAXN],dep[MAXN],vis[MAXN],sta[MAXN];

void Add_Edge (int u,int v){
	G[u].push_back (v),
	G[v].push_back (u);
}

void dfs (int u,int fa){
	bool flag = 0;
	sta[++ top] = u,vis[u] = 1;
	for (Int i = 0;i < G[u].size();++ i){
		int v = G[u][i];
		if (v == fa && !flag){
			flag = 1;
			continue;
		}
		if (vis[v] == 1){
			int cnt = 1;
			while (sta[top] != v){
				cnt ++;
				vis[sta[top --]] = 0;
			}
			sg[v] ^= (cnt & 1);
		}
		else if (vis[v] == -1){
			dfs (v,u); 
			if (vis[v]) sg[u] ^= sg[v] + 1;
		}
	}
	if (vis[u]) -- top;
	return ;
}

void clear (){
	top = 0;
	for (Int i = 1;i <= n;++ i) sg[i] = dep[i] = 0,vis[i] = -1,G[i].clear ();
}

signed main(){
	while (~scanf ("%d",&T)){
		int ans = 0;
		while (T --> 0){
			read (n),read (m),clear ();
			for (Int i = 1,u,v;i <= m;++ i){
				read (u),read (v);
				if (u ^ v) Add_Edge (u,v);
			}
			dfs (1,0),ans ^= sg[1];
		}
		puts (ans ? "Sally" : "Harry");
	} 
	return 0;
}
posted @ 2020-09-28 22:02  Dark_Romance  阅读(215)  评论(0编辑  收藏  举报