• 【luogu AT2376】Black and White Tree(结论)(博弈论)(二分图)


    Black and White Tree

    题目链接:luogu AT2376

    题目大意

    给你一棵树,每次先手后手轮流选一个点染上自己的颜色。
    然后如果先手的一次染色中它染色的点连着的边都是先手染过色的,那先手就赢了。
    然后要你判断是先手必胜还是后手必胜。

    思路

    这题的结论是如果这个图的最大匹配能覆盖所有点,就是后手必胜,否则是先手必胜。

    下面简单证明:如果有最大匹配是全部点,那先手无论点什么点,后手只要点跟它匹配的点。
    那因为是匹配,是相邻的点,所以所有的白点旁边一定会有点都是黑点。(跟它匹配的点)

    那如果最大匹配不是整个图,就会多出一个点。
    我们可以安排这个点的位置到叶子节点,然后先手把它连着的点点了,后手不能叶子结点点,而是要点你那个点匹配的点(否则你别的地方就可以成立了),然后你就可以点这个叶子结点赢了。

    然后判断树的最大匹配可以树形 DP,也可以直接无脑黑白染色网络流。

    代码

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define INF 0x3f3f3f3f3f3f3f3f 
    
    using namespace std;
    
    struct node {
    	int to, nxt;
    }e[200001];
    int n, x, y, deg[200001];
    int num[2], le[200001], KK;
    
    struct nde {
    	int x, to, nxt, op;
    }e_[600001];
    int le_[200005], KK_, S, T;
    int dis[200005];
    
    void add(int x, int y) {
    	e[++KK] = (node){y, le[x]}; le[x] = KK;
    	e[++KK] = (node){x, le[y]}; le[y] = KK;
    }
    
    void add_(int x, int y, int z) {
    	e_[++KK_] = (nde){z, y, le_[x], KK_ + 1}; le_[x] = KK_;
    	e_[++KK_] = (nde){0, x, le_[y], KK_ - 1}; le_[y] = KK_;
    }
    
    void dfs(int now, int father) {
    	deg[now] = deg[father] + 1;
    	if (deg[now] & 1) {
    		add_(S, now, 1);
    		add_(now, father, 1);
    		for (int i = le[now]; i; i = e[i].nxt)
    			if (e[i].to != father)
    				add_(now, e[i].to, 1);
    	}
    	else add_(now, T, 1);
    	for (int i = le[now]; i; i = e[i].nxt)
    		if (e[i].to != father) dfs(e[i].to, now); 
    }
    
    bool bfs() {//网络流求最大匹配(其实也可以 DP)
    	memset(dis, 0x7f, sizeof(dis));
    	dis[S] = 0;
    	queue <int> q;
    	q.push(S);
    	while (!q.empty()) {
    		int now = q.front();
    		q.pop();
    		for (int i = le_[now]; i; i = e_[i].nxt)
    			if (e_[i].x && dis[e_[i].to] > dis[now] + 1) {
    				dis[e_[i].to] = dis[now] + 1;
    				if (e_[i].to == T) return 1;
    				q.push(e_[i].to);
    			}
    	}
    	return 0;
    }
    
    int dfss(int now, int sum) {
    	if (now == T) return sum;
    	int go = 0;
    	for (int i = le_[now]; i; i = e_[i].nxt)
    		if (dis[e_[i].to] == dis[now] + 1 && e_[i].x) {
    			int this_go = dfss(e_[i].to, min(sum - go, e_[i].x));
    			if (this_go) {
    				e_[i].x -= this_go;
    				e_[e_[i].op].x += this_go;
    				go += this_go;
    				if (go == sum) return go;
    			}
    		}
    	return go;
    }
    
    int dinic() {
    	int re = 0;
    	while (bfs())
    		re += dfss(S, INF);
    	return re;
    } 
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i < n; i++) {
    		scanf("%d %d", &x, &y);
    		add(x, y);
    	}
    	
    	S = n + 1; T = S + 1;
    	dfs(1, 0);
    	
    	if (n & 1) {
    		printf("First
    ");
    		return 0;
    	}
    	if (dinic() * 2 != n) {
    		printf("First
    ");
    	}
    	else printf("Second
    ");
    	
    	return 0;
    }
    
  • 相关阅读:
    【JAVA】java 堆溢出分析
    【数据结构】链表的基本学习 实现
    【其他】博客园自定义样式指南
    【JAVA】一些好用的开源java类库
    【Shell】Shell 常用命令
    【Spring】Spring Boot 要点整理
    【数据库】Mysql 连接相关
    【Linux】Shell 参数解析
    Erlang 中类型转换
    erlang 中 maps 练习
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_AT2376.html
Copyright © 2020-2023  润新知