算法笔记--sg函数详解及其模板
算法笔记
参考资料:https://wenku.baidu.com/view/25540742a8956bec0975e3a8.html
sg函数大神详解:http://blog.csdn.net/luomingjun12315/article/details/45555495
sg[i]定义,从i走一步能到达的j的sg[j]以外的最小值,那么从sg函数值为x的状态出发,我们能转移到sg值为0,1,...,x-1的状态
对于某个人来说,0是他的必败态,sg[0] = 0
我们从这个状态出发,用dp求sg函数的值
sg[n] = 0,表示必败,否则, 表示必胜
如果sg[n] > 0,说明肯定能转移到必败态,则必胜
如果sg[n] = 0, 说明无论怎么转移都是必胜态,则必败
模板:
int f[N],SG[N];
bool S[M];
void getSG(int n)
{
memset(SG,,sizeof(SG));
for(int i=;i<=n;i++)
{
memset(S,false,sizeof(S));
for(int j=;f[j]<=i&&j<M;j++)
{
S[SG[i-f[j]]]=true;
}
while(S[SG[i]]) SG[i]++;
}
}
例题:http://www.cnblogs.com/widsom/p/7171428.html
http://www.cnblogs.com/widsom/p/7170891.html
sg函数拓展:
反sg博弈:
先手必胜:(所有单一局面sg值都不超过1&&总局面sg值为0) || (存在一个单一局面sg值超过1&&总局面sg值不为0)
否则后手必胜。
代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n"; const int N = , M = 5e3 + ;
int a[N], sg[M], T, n;
int main() {
for (int i = ; i < M; ++i) sg[i] = i;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for (int i = ; i <= n; ++i) scanf("%d", &a[i]);
int cnt = , s = ;
for (int i = ; i <= n; ++i) {
if(sg[a[i]] > ) ++cnt;
s ^= sg[a[i]];
}
if((!cnt && !s) || (cnt && s)) printf("John\n");
else printf("Brother\n");
}
return ;
}
树上删边博弈:
定理:叶子节点的sg值为0,其他节点u的sg[u]值等于它儿子v的(sg[v]+1)的亦或和。
图上删边博弈:
将偶环缩成点,奇环缩成一个点加一条边,就可以转换成树上删边博弈了。
具体证明看最上面的链接。
思路:树上删边博弈
代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n"; const int N = 1e5 + ;
vector<int> g[N];
int T, n, u, v;
int sg(int u, int o) {
int res = ;
for (int i = ; i < g[u].size(); ++i) {
int v = g[u][i];
if(v != o) res ^= sg(v, u) + ;
}
return res;
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for (int i = ; i < n; ++i) scanf("%d %d", &u, &v), g[u].pb(v), g[v].pb(u);
if(sg(, )) printf("Alice\n");
else printf("Bob\n");
for (int i = ; i <= n; ++i) g[i].clear();
}
return ;
}
思路:tarjan缩边双转换成树上删边博弈
代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n"; const int N = ;
vector<int> g[N];
int t, n, m, u, v;
int stk[N], sg[N], low[N], dfn[N], cnt = , top = ;
bool vis[N], vv[N];//vv标记环上的点是否被删掉
void tarjan(int u, int o) {
dfn[u] = low[u] = ++cnt;
stk[++top] = u;
vv[u] = vis[u] = true;
for (int i = ; i < g[u].size(); ++i) {
int v = g[u][i];
if(v == o) continue;
if(!dfn[v]) tarjan(v, u), low[u] = min(low[u], low[v]);
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u]) {
int c = ;
while(stk[top] != u) {
vv[stk[top]] = false;
vis[stk[top--]] = false;
++c;
}
vis[stk[top--]] = false;
++c;
if(c > && c%) sg[u] ^= ;
}
for (int i = ; i < g[u].size(); ++i) {
int v = g[u][i];
if(v == o) continue;
if(vv[v]) sg[u] ^= sg[v]+;
}
}
int main() {
while(~scanf("%d", &t)) {
int s = ;
while(t--) {
scanf("%d %d", &n, &m);
for (int i = ; i < m; ++i) {
scanf("%d %d", &u, &v);
g[u].pb(v);
g[v].pb(u);
}
tarjan(, );
s ^= sg[];
for (int i = ; i <= n; ++i) low[i] = dfn[i] = sg[i] = vis[i] = vv[i] = ;
cnt = top = ;
for (int i = ; i <= n; ++i) g[i].clear();
}
if(s) printf("Sally\n");
else printf("Harry\n");
}
return ;
}
思路:
圆扫描线+树上删边博弈
代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n"; const int N = 2e4 + ;
int nowx;
struct circle {
int x, y, r;
}p[N];
double Y(int id, int ty) {
if(ty == ) return p[id].y - sqrt(p[id].r*1.0*p[id].r - (nowx-p[id].x)*1.0*(nowx-p[id].x));
else return p[id].y + sqrt(p[id].r*1.0*p[id].r - (nowx-p[id].x)*1.0*(nowx-p[id].x));
}
struct node {
int id, ty;
bool operator < (const node &rhs) const {
if(id == rhs.id) return ty < rhs.ty;
else return Y(id, ty) < Y(rhs.id, rhs.ty);
}
};
set<node> s;
vector<int> g[N];
int T, n, dp[N], fa[N], sg[N];
piii t[N*];
void dfs(int u, int o) {
sg[u] = ;
for (int i = ; i < g[u].size(); ++i) {
int v = g[u][i];
if(v != o) {
dfs(v, u);
sg[u] ^= sg[v] + ;
}
}
}
int main() {
p[].x = p[].y = ;
p[].r = ;
s.insert({, });
s.insert({, });
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for (int i = ; i <= n; ++i) scanf("%d %d %d", &p[i].x, &p[i].y, &p[i].r);
for (int i = ; i <= n; ++i) {
t[i].fi.fi = p[i].x - p[i].r;
t[i].fi.se = ;
t[i].se = i;
t[n+i].fi.fi = p[i].x + p[i].r;
t[n+i].fi.se = ;
t[n+i].se = i;
}
sort(t+, t++*n);
for (int i = ; i <= *n; ++i) {
nowx = t[i].fi.fi;
int id = t[i].se;
node tmp = {id, };
if(t[i].fi.se == ) {
auto l = s.lower_bound(tmp); --l;
auto r = s.upper_bound(tmp);
if((*l).id == (*r).id) {
dp[id] = dp[(*l).id] + ;
fa[id] = (*l).id;
}
else if(dp[(*l).id] >= dp[(*r).id]) {
dp[id] = dp[(*l).id];
fa[id] = fa[(*l).id];
}
else {
dp[id] = dp[(*r).id];
fa[id] = fa[(*r).id]; }
g[fa[id]].pb(id);
s.insert({id, });
s.insert({id, });
}
else {
s.erase({id, });
s.erase({id, });
}
}
dfs(, );
if(sg[]) printf("Alice\n");
else printf("Bob\n");
for (int i = ; i <= n; ++i) g[i].clear(), sg[i] = fa[i] = dp[i] = ;
}
return ;
}
思路:
出题人真是个机灵鬼,将反-sg和树上删边结合起来,大概是看了论文后才出的题(雾
代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#include<bits/stdc++.h>
using namespace std;
#define y1 y11
#define fi first
#define se second
#define pi acos(-1.0)
#define LL long long
//#define mp make_pair
#define pb push_back
#define ls rt<<1, l, m
#define rs rt<<1|1, m+1, r
#define ULL unsigned LL
#define pll pair<LL, LL>
#define pli pair<LL, int>
#define pii pair<int, int>
#define piii pair<pii, int>
#define pdd pair<double, double>
#define mem(a, b) memset(a, b, sizeof(a))
#define debug(x) cerr << #x << " = " << x << "\n"; const int N = ;
vector<int> g[N];
int t, n, u, v;
int dfs(int u, int o) {
int sg = ;
for (int i = ; i < g[u].size(); ++i) {
int v = g[u][i];
if(v != o) sg ^= dfs(v, u) + ;
}
return sg;
}
int main() {
while(~scanf("%d", &t)) {
int cnt = , s = ;
while(t--) {
scanf("%d", &n);
for (int i = ; i < n; ++i) scanf("%d %d", &u, &v), g[u].pb(v), g[v].pb(u);
int sg = dfs(, );
s ^= sg;
if(sg > ) ++cnt;
for (int i = ; i <= n; ++i) g[i].clear();
}
if((cnt && s) || (!cnt && !s)) printf("PP\n");
else printf("QQ\n");
}
return ;
}
算法笔记--sg函数详解及其模板的更多相关文章
- 【基础操作】博弈论 / SG 函数详解
博弈死我了……(话说哪个小学生会玩博弈论提到的这类弱智游戏,还取石子) 先推荐两个文章链接:浅谈算法——博弈论(从零开始的博弈论) 博弈论相关知识及其应用 This article was updat ...
- Python学习笔记:函数详解(下)
本文介绍:高阶函数,嵌套函数,以及由前面两个组成的装饰器 一.高阶函数:以下两种情况都是高阶函数 1.将函数名当成参数传递给另外一个函数(作用:不修改被传递函数源代码就可以添加新功能): impo ...
- Angular6 学习笔记——组件详解之模板语法
angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...
- C++ 虚函数详解
C++ 虚函数详解 这篇文章主要是转载的http://blog.csdn.net/haoel/article/details/1948051这篇文章,其中又加入了自己的理解和难点以及疑问的解决过程,对 ...
- CreateFile函数详解
CreateFile函数详解 CreateFile The CreateFile function creates or opens the following objects and returns ...
- softmax函数详解
答案来自专栏:机器学习算法与自然语言处理 详解softmax函数以及相关求导过程 这几天学习了一下softmax激活函数,以及它的梯度求导过程,整理一下便于分享和交流. softmax函数 softm ...
- [转]softmax函数详解
答案来自专栏:机器学习算法与自然语言处理 详解softmax函数以及相关求导过程 这几天学习了一下softmax激活函数,以及它的梯度求导过程,整理一下便于分享和交流. softmax函数 softm ...
- # OpenGL常用函数详解(持续更新)
OpenGL常用函数详解(持续更新) 初始化 void glutInit(int* argc,char** argv)初始化GULT库,对应main函数的两个参数 void gultInitWindo ...
- python 排序算法总结及实例详解
python 排序算法总结及实例详解 这篇文章主要介绍了python排序算法总结及实例详解的相关资料,需要的朋友可以参考下 总结了一下常见集中排序的算法 排序算法总结及实例详解"> 归 ...
随机推荐
- try except else
try except 语句还有一个可选的else子句,如果使用这个子句,那么必须放在所有的except子句之后.这个子句将在try子句没有发生任何异常的时候执行.例如: for arg in sys. ...
- Apache-Shiro介绍
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大的网络 ...
- .Net Core2.0基于DbContext,IActionFilter过滤器实现全局UOW,不使用TransactionScope
抛弃TransactionScope 之前实现过类似功能是使用的TransactionScope,总碰到这样那样的问题,新项目迁移到.net core2.0下,果断抛弃之前的写法,因为DbContex ...
- HDU 4770
这题说的是一在一个N*M的房间内,然后有些房间不能被灯光照亮,有一个灯可以转动方向,其他的灯只能在固定一个方向上,因为数据比较小,所以比较水,直接暴力的进行枚举就好了,但是还是 wa了很久,原因没认真 ...
- appium记录
npm uninstall appium -g npm install -g cnpm --registry=https://registry.npm.taobao.org cnpm install ...
- js 内置对象和方法 示例
JS内置函数不从属于任何对象,在JS语句的任何地方都可以直接使用这些函数.JS中常用的内置函数如下: 1.eval(str)接收一个字符串形式的表达式,并试图求出表达式的值.作为参数的表达式可以采用任 ...
- MySQL Crash Course #01# Chapter 1. 2 概念. Primary key
索引 database table schema Primary Key MySQL 书的第一章介绍一些基本的概念.理解数据库是掌握 MySQL 非常重要的一个部分. 第二章简单介绍了 MySQL 以 ...
- Python Web学习笔记之IGMP和ICMP的差别
理论技术:TCP/IP协议族(四)ICMP和IGMP协议! 应该先说IP协议的,后来考虑到层次性,还是先把支撑协议介绍完在细说IP!因为IP是我的最爱也是我的痛!呵呵! 一.ICMP协议 为什么要使用 ...
- 车载项目问题解(memset)
1memset函数解 1.void *memset(void *s,int c,size_t n) 总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c.2.例子 #includevoid ...
- vim的个性化配置- 再谈vim的折叠和展开 -- 彻底掌握vim 的展开和折叠!
http://www.wklken.me/posts/2016/02/03/some-vim-configs.html 一般把 设置成 逗号, 是比较好的, 因为逗号比默认的leader 要方便键入 ...