【HDU - 5963】朋友(博弈,思维,必胜态必败态,找规律)

题干:

B君在围观一群男生和一群女生玩游戏,具体来说游戏是这样的:
给出一棵n个节点的树,这棵树的每条边有一个权值,这个权值只可能是0或1。 在一局游戏开始时,会确定一个节点作为根。接下来从女生开始,双方轮流进行 操作。
当一方操作时,他们需要先选择一个不为根的点,满足该点到其父亲的边权为1; 然后找出这个点到根节点的简单路径,将路径上所有边的权值翻转(即0变成1,1 变成0 )。
当一方无法操作时(即所有边的边权均为0),另一方就获得了胜利。
如果在双方均采用最优策略的情况下,女生会获胜,则输出“Girls win!”,否则输 出“Boys win!”。
为了让游戏更有趣味性,在每局之间可能会有修改边权的操作,而且每局游戏指 定的根节点也可能是不同的。
具体来说,修改边权和进行游戏的操作一共有m个,具体如下:
∙∙“0 x”表示询问对于当前的树,如果以x为根节点开始游戏,哪方会获得胜利。
∙∙“1 x y z ”表示将x和y之间的边的边权修改为z。
B君当然知道怎么做啦!但是他想考考你。

Input

包含至多5组测试数据。
第一行有一个正整数,表示数据的组数。
接下来每组数据第一行,有二个空格隔开的正整数n,m,分别表示点的个数,操 作个数。保证n,m< 40000。
接下来n-1行,每行三个整数x,y,z,表示树的一条边。保证1 接下来m行,每行一个操作,含义如前所述。保证一定只会出现前文中提到的两 种格式。
对于操作0,保证1 <= x <= n ;对于操作1,保证1 <= x <= n, 1 <= y <= n, 0 <= z <= 1,保证树上存在一条边连接x和y。

Output

对于每组数据的每一个询问操作,输出一行“Boys win!”或者“Girls win!”。

Sample Input

2
2 3
1 2 0
0 1
1 2 1 1
0 2
4 11
1 2 1
2 3 1
3 4 0
0 1
0 2
0 3
0 4
1 2 1 0
0 1
0 2
0 3
1 3 4 1
0 3
0 4

Sample Output

Boys win!
Girls win!
Girls win!
Boys win!
Girls win!
Boys win!
Boys win!
Girls win!
Girls win!
Boys win!
Girls win! 

解题报告:

想想他需要不停的换根,数据量又如此庞大,以至于每次换根后,我dfs一遍的时间都没有,所以肯定是个找规律题,肯定不会给你遍历整棵树的机会的。从必胜态必败态的角度考虑,不难发现如果是一条链的情况,规律就是根节点相连的那条边的边权如果是1,那女生胜,反之男生胜。又因为如果是多条链的话,链之间互不影响,所以可以直接统计和根节点相邻的边的边权和,如果是奇数,那就是女生获胜,反之男生获胜。当然如果先一条链然后分成两条链的情况,那其实也问题不大,因为发现可以转化成一条链的情况上面。(其实可以大胆猜想,既然一条链的情况,只和根节点相邻的边的边权有关系,假设这条边是e,那也就是所有 想到达根节点的路径,只要需要经过边e,那就都和路径上的其他边没关系,之和边e相关,所以就这样?)

好吧,还是来一发正解:

正解是,经分析发现无论操作哪个节点,这个节点都会使其所在子树中与根直接相连的那条边翻转。那么再根据游戏的规则以及子树的性质,会发现若你面对当前这条与根相连的边的权值为1时,对方通过子树上任意点来翻转当前边,你都能再次进行翻转。如果你面对权值为0,那么要么你不能进行操作,要么不管你进行什么操作对方都还能进行操作。

AC代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FF first
#define SS second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair PII;
const int MAX = 2e5 + 5;
vector vv[MAX];
int n,m; 
int main()
{
    int T;
    cin>>T;
    while(T--) {
        scanf("%d%d",&n,&m);
        for(int i = 1; i<=n; i++) vv[i].clear(); 
        for(int x,y,z,i = 1; i<=n-1; i++) {
            scanf("%d%d%d",&x,&y,&z);
            vv[x].pb(pm(y,z));vv[y].pb(pm(x,z));
        }
        for(int i = 1; i<=n; i++) sort(vv[i].begin(),vv[i].end());
        int op,u,v,z,ans;
        while(m--) {
            scanf("%d",&op);
            if(op == 0) {
                ans=0; scanf("%d",&u);
                for(auto x : vv[u]) {
                    if(x.SS == 1) ans++;
                }
                if(ans & 1) puts("Girls win!");
                else puts("Boys win!");
            }
            else {
                scanf("%d%d%d",&u,&v,&z);
                int pos1 = lower_bound(vv[u].begin(),vv[u].end(),pm(v,-1)) - vv[u].begin();
                int pos2 = lower_bound(vv[v].begin(),vv[v].end(),pm(u,-1)) - vv[v].begin();
                vv[u][pos1] = pm(v,z); vv[v][pos2] = pm(u,z);
            }
        }
    } 


    return 0 ;
}

关于实现细节:

发现对于0操作,如果是菊花图的话就炸了。

所以优化一下,发现不需要枚举:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FF first
#define SS second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair PII;
const int MAX = 2e5 + 5;
vector vv[MAX];
int n,m,in[MAX];
int main()
{
    int T;
    cin>>T;
    while(T--) {
        scanf("%d%d",&n,&m);
        for(int i = 1; i<=n; i++) vv[i].clear(),in[i]=0; 
        for(int x,y,z,i = 1; i<=n-1; i++) {
            scanf("%d%d%d",&x,&y,&z);
            vv[x].pb(pm(y,z));vv[y].pb(pm(x,z));
            if(z == 1) in[x]++,in[y]++; 
        }
        for(int i = 1; i<=n; i++) sort(vv[i].begin(),vv[i].end());
        int op,u,v,z,ans;
        while(m--) {
            scanf("%d",&op);
            if(op == 0) {
                ans=0; scanf("%d",&u);
                if(in[u] & 1) puts("Girls win!");
                else puts("Boys win!");
            }
            else {
                scanf("%d%d%d",&u,&v,&z);
                int pos1 = lower_bound(vv[u].begin(),vv[u].end(),pm(v,-1)) - vv[u].begin();
                int pos2 = lower_bound(vv[v].begin(),vv[v].end(),pm(u,-1)) - vv[v].begin();
                if(vv[u][pos1].SS==0 && z == 1) {
                    vv[u][pos1] = pm(v,z); vv[v][pos2] = pm(u,z);in[u]++,in[v]++;
                }
                else if(vv[u][pos1].SS==1 && z == 0) {
                    vv[u][pos1] = pm(v,z); vv[v][pos2] = pm(u,z);in[u]--,in[v]--;
                }                
            }
        }
    } 
    return 0 ;
}

 

你可能感兴趣的:(HDU,博弈问题)