算法笔记–sg函数详解及其模板

算法笔记–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,0,sizeof(SG));
    for(int i=1;i<=n;i++)
    {
        memset(S,false,sizeof(S));
        for(int j=1;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)

否则后手必胜。

HDU 1907

代码:

算法笔记--sg函数详解及其模板
算法笔记--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 = 55, M = 5e3 + 5;
int a[N], sg[M], T, n;
int main() {
    for (int i = 0; i < M; ++i) sg[i] = i;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
        int cnt = 0, s = 0;
        for (int i = 1; i <= n; ++i) {
                if(sg[a[i]] > 1) ++cnt;
                s ^= sg[a[i]];
        }
        if((!cnt && !s) || (cnt && s)) printf("John\n");
        else printf("Brother\n");
    }
    return 0;
}

View Code

树上删边博弈:

定理:叶子节点的sg值为0,其他节点u的sg[u]值等于它儿子v的(sg[v]+1)的亦或和。

图上删边博弈:

将偶环缩成点,奇环缩成一个点加一条边,就可以转换成树上删边博弈了。

具体证明看最上面的链接。

HDU 3094

思路:树上删边博弈

代码:

算法笔记--sg函数详解及其模板
算法笔记--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 = 1e5 + 5;
vector<int> g[N];
int T, n, u, v;
int sg(int u, int o) {
    int res = 0;
    for (int i = 0; i < g[u].size(); ++i) {
        int v = g[u][i];
        if(v != o) res ^= sg(v, u) + 1;
    }
    return res;
}
int main() {
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for (int i = 1; i < n; ++i) scanf("%d %d", &u, &v), g[u].pb(v), g[v].pb(u);
        if(sg(1, 1)) printf("Alice\n");
        else printf("Bob\n");
        for (int i = 1; i <= n; ++i) g[i].clear();
    }
    return 0;
}

View Code

POJ 3710

思路:tarjan缩边双转换成树上删边博弈

代码:

算法笔记--sg函数详解及其模板
算法笔记--sg函数详解及其模板

#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 = 105;
vector<int> g[N];
int t, n, m, u, v;
int stk[N], sg[N], low[N], dfn[N], cnt = 0, top = 0;
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 = 0; 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 = 0;
        while(stk[top] != u) {
            vv[stk[top]] = false;
            vis[stk[top--]] = false;
            ++c;
        }
        vis[stk[top--]] = false;
        ++c;
        if(c > 1 && c%2) sg[u] ^= 1;
    }
    for (int i = 0; i < g[u].size(); ++i) {
        int v = g[u][i];
        if(v == o) continue;
        if(vv[v]) sg[u] ^= sg[v]+1;
    }
}
int main() {
    while(~scanf("%d", &t)) {
        int s = 0;
        while(t--) {
            scanf("%d %d", &n, &m);
            for (int i = 0; i < m; ++i) {
                scanf("%d %d", &u, &v);
                g[u].pb(v);
                g[v].pb(u);
            }
            tarjan(1, 1);
            s ^= sg[1];
            for (int i = 1; i <= n; ++i) low[i] = dfn[i] = sg[i] = vis[i] = vv[i] = 0;
            cnt = top = 0;
            for (int i = 1; i <= n; ++i) g[i].clear();
        }
        if(s) printf("Sally\n");
        else printf("Harry\n");
    }
    return 0;
}

View Code

 HDU 5299

思路:

圆扫描线+树上删边博弈

代码:

算法笔记--sg函数详解及其模板
算法笔记--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 = 2e4 + 5;
int nowx;
struct circle {
    int x, y, r;
}p[N];
double Y(int id, int ty) {
    if(ty == 0) 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*2];
void dfs(int u, int o) {
    sg[u] = 0;
    for (int i = 0; i < g[u].size(); ++i) {
        int v = g[u][i];
        if(v != o) {
            dfs(v, u);
            sg[u] ^= sg[v] + 1;
        }
    }
}
int main() {
    p[0].x = p[0].y = 0;
    p[0].r = 100000;
    s.insert({
     
     0, 0});
    s.insert({
     
     0, 1});
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) scanf("%d %d %d", &p[i].x, &p[i].y, &p[i].r);
        for (int i = 1; i <= n; ++i) {
            t[i].fi.fi = p[i].x - p[i].r;
            t[i].fi.se = 0;
            t[i].se = i;
            t[n+i].fi.fi = p[i].x + p[i].r;
            t[n+i].fi.se = 1;
            t[n+i].se = i;
        }
        sort(t+1, t+1+2*n);
        for (int i = 1; i <= 2*n; ++i) {
            nowx = t[i].fi.fi;
            int id = t[i].se;
            node tmp = {id, 1};
            if(t[i].fi.se == 0) {
                auto l = s.lower_bound(tmp); --l;
                auto r = s.upper_bound(tmp);
                if((*l).id == (*r).id) {
                    dp[id] = dp[(*l).id] + 1;
                    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, 1});
                s.insert({id, 0});
            }
            else {
                s.erase({id, 1});
                s.erase({id, 0});
            }
        }
        dfs(0, 0);
        if(sg[0]) printf("Alice\n");
        else printf("Bob\n");
        for (int i = 0; i <= n; ++i) g[i].clear(), sg[i] = fa[i] = dp[i] = 0;
    }
    return 0;
}

View Code

 HDU 3590

思路:

出题人真是个机灵鬼,将反-sg和树上删边结合起来,大概是看了论文后才出的题(雾

代码:

算法笔记--sg函数详解及其模板
算法笔记--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 = 105;
vector<int> g[N];
int t, n, u, v;
int dfs(int u, int o) {
    int sg = 0;
    for (int i = 0; i < g[u].size(); ++i) {
        int v = g[u][i];
        if(v != o) sg ^= dfs(v, u) + 1;
    }
    return sg;
}
int main() {
    while(~scanf("%d", &t)) {
        int cnt = 0, s = 0;
        while(t--) {
            scanf("%d", &n);
            for (int i = 1; i < n; ++i) scanf("%d %d", &u, &v), g[u].pb(v), g[v].pb(u);
            int sg = dfs(1, 1);
            s ^= sg;
            if(sg > 1) ++cnt;
            for (int i = 0; i <= n; ++i) g[i].clear();
        }
        if((cnt && s) || (!cnt && !s)) printf("PP\n");
        else printf("QQ\n");
    }
    return 0;
}

View Code

 

转载于:https://www.cnblogs.com/widsom/p/7172386.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108318.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • ue4地编教程_编绳方法

    ue4地编教程_编绳方法一、基本操作二、导入模型和贴图##(一)导入UE4设置:1、导入:2、导入设置##(二)从3DMAX出设置##(三)从Blender导出

    2022年9月26日
    2
  • SpringBoot面试题整理,常问SpringBoot面试题汇总(2020版)

    SpringBoot面试题整理,常问SpringBoot面试题汇总(2020版)找工作的历程太艰难,面试的过程很心烦,在没着落的每一天,心情都不太美妙,这时的我们唯一能做的就是多总结,多做准备,这样,起码心里会好受些!所以我准备了一点SpringBoot的面试题,为还正在找工作的小伙伴多增加些成功的筹码!1、什么是SpringBoot?SpringBoot是Spring开源组织下的子项目,是Spring组件一站式解决方案,主要是简化了使用Spring的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。2、SpringBoot有哪些优点?.

    2022年5月21日
    34
  • 无向图同构 (哈希)「建议收藏」

    题目ProblemDescription如果一个无向图重标号后与另一个无向图完全一致(即对于任意两点,他们之间的边在两个图中都存在或都不存在),则称两个无向图同构。给定两个n个点m条边的无向图,判定两个无向图是否同构。Input第一行一个数T,表示有T组数据(T<=20)对于每一组数据:

    2022年4月17日
    62
  • 伪元素的作用_获取iframe中的元素

    伪元素的作用_获取iframe中的元素目标网站红薯中文网获取网页源代码也获取不了这些动态渲染的数据所以用简单的,但是有点麻烦的方法使用selenium执行js,或者直接在浏览器里面执行jsfunctionkkk(){varmyIdElement=document.getElementsByClassName(“context_kw11″);varbeforeStyle=window.getComputedStyle(myIdElement[0],”::before”);returnbeforeStyle.con

    2022年10月12日
    4
  • DVP和MIPI接口的简单区别

    DVP和MIPI接口的简单区别区别1、DVP接口:DVP是并行传输,传输速度较慢,传输的带宽低。2、MIPI接口:MIPI是差分串行传输,速度快,抗干扰。目前分为D/C/MPHY三类。主流手机模组现在是使用MIPI_DPHY或CPHY传输:DPHY传输时使用4对差分信号传输图像数据和一对差分时钟信号。CPHY使用3组每组3根单端信号传输数据,每根单端信号能表达3个逻辑电平,相比数据传输率更高,使用引脚数更少。1、DVP接口:使用需要PCLK\sensor输出时钟、MCLK(XCLK)\外部时钟输入、VSYNC\场同步、

    2022年6月1日
    159
  • js JavaScript vue 时间戳 转换 日期 YYYY-MM-DD hh:mm:ss 简洁写法

    js JavaScript vue 时间戳 转换 日期 YYYY-MM-DD hh:mm:ss 简洁写法两种方法方法一使用两个apitoLocaleDateString()和toTimeString()加正则表达式,简洁写法,推荐!还可以更改为以点(.)连接——正则表达式代码letnewDate=newDate();this.date=newDate.toLocaleDateString().replace(/\//g,”-“)+””+newDate.toTimeString().substr(0,8);结果缺点月份不能是03的形式

    2022年8月30日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号