HackerRank - number-game-on-a-tree

Andy and Lily love playing games with numbers and trees. Today they have a treeconsisting of  nodes and  edges. Each edge  has an integer weight, .

Before the game starts, Andy chooses an unordered pair of distinct nodes, , and uses all the edge weights present on the unique path from node  to node  to construct a list of numbers. For example, in the diagram below, Andy constructs a list from the edge weights along the path :

image

Andy then uses this list to play the following game with Lily:

  • Two players move in alternating turns, and both players play optimally (meaning they will not make a move that causes them to lose the game if some better, winning move exists).
  • Andy always starts the game by removing a single integer from the list.
  • During each subsequent move, the current player removes an integer less than or equal to the integer removed in the last move.
  • The first player to be unable to move loses the game.

For example, if the list of integers is  and Andy starts the game by removing , the list becomes . Then, in Lily's move, she must remove a remaining integer less than or equal to  (i.e., , or ).

The two friends decide to play  games, where each game is in the form of a tree. For each game, calculate the number of unordered pairs of nodes that Andy can choose to ensure he always wins the game.

Input Format

The first line contains a single integer, , denoting the number of games. The subsequent lines describe each game in the following format:

  1. The first line contains an integer, , denoting the number of nodes in the tree.
  2. Each line  of the  subsequent lines contains three space-separated integers describing the respective values of , and  for the  edge connecting nodes  and  with weight .

Constraints

  • Sum of  over all games does not exceed 

Scoring

  • For  of score, the sum of  over all games does not exceed .
  • For  of score, the sum of  over all games does not exceed .

Output Format

For each game, print an integer on a new line describing the number of unordered pairs Andy can choose to construct a list that allows him to win the game.

Sample Input 0

3
5
1 2 2
1 3 1
3 4 1
3 5 2
5
1 2 0
2 3 2
3 4 2
4 5 0
5
1 2 0
1 3 1
3 4 0
3 5 2

Sample Output 0

9
8
10

Explanation 0

Andy and Lily play the following  games:

  1. The first game's tree looks like this: 
    image

    There are  ways to choose , and only one such pair causes Andy to lose the game. If he chooses the pair , the list is . Andy removes  in his first move, and Lily removes the remaining  in the next move; at this point, Andy has no remaining moves and Lily wins. Because Andy will win if he selects any of the other  pairs, we print  on a new line.

  2. The second game's tree looks like this: 
    image

    There are  ways to choose , and two pairs that cause Andy to lose the game:

    • If Andy chooses , the list is . Andy removes  in his first move, and Lily removes the remaining  in the next move; at this point, Andy has no remaining moves and Lily wins.
    • If Andy chooses , the list is . Andy can remove either a  or a  in the first move, but either way Lily will make an optimal choice in her next move that causes Andy to lose. Andy will win if he selects any of the other  pairs, so we print  on a new line.
  3. The third game's tree looks like this: 
    image 
    There are  ways to choose , and Andy will win the game regardless of which pair he chooses. Thus, we print  on a new line.


此题问你选取两个点,玩游戏,先手必赢的点对有多少个。

玩游戏的规则:

1.玩家每次只能选择小于等于上一次选择的数值的大小。

2.知道某一个玩家无法选择的时候,这个玩家输掉比赛。

先手必赢的情况不难想,只要先手选择一个最小的就是必赢了。但是最小的可能是有偶数个的。

所以再扩展一下就是,如果所有的数都是偶数个,那么先手是必输的(这时后手只需要跟着先手拿就可以了),反之,只要有一个数是奇数个,那么必定先手必赢。(这时,先手拿掉那个奇数,如果有多个奇数,那么只要拿掉那个最小的奇数,后面先手跟着拿就可以了)。

然后,我们直接找存在奇数的不太好搞,相反的点对情况就是所有的数的个数为偶数个。这时候就可以想到异或。但是,并不是异或为0就必定是每个数的个数都是偶数。3个数异或也有可能为0的。这里就直接学习了一个哈希函数。把边重新映射,然后再去异或就没问题了。

相同的两个数异或值为0的。

我们去深搜全部的路径,并且同时异或权值,然后记录每个权值出现的次数。每当一个数出现两次,就会多一个点对,当然组合起来的话,肯定是至少多一个点对啦。

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
int n,m,q;
const int MAXN=5e5+7;
const int mod=1e9+7;
vector<pair<int,ULL>>edge[MAXN];
map<ULL,int>num;

ULL Hash(ULL w)
{
    return w*747474747+447474747;
}

void dfs(int u,int fa,ULL w)
{
    num[w]++;
    for(auto it:edge[u])
    {
        int v=it.first;
        ULL ww=it.second;
        if(v==fa)continue;
        dfs(v,u,w^ww);
    }
}
int main()
{
    int i;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;++i)edge[i].clear();
        int u,v,w;
        for(i=0;i<n-1;++i)
        {
            scanf("%d%d%d",&u,&v,&w);
            edge[u].push_back(make_pair(v,Hash(w)));
            edge[v].push_back(make_pair(u,Hash(w)));
        }
        ULL ans=(1ULL*n*(n-1))>>1;
        num.clear();
        dfs(1,0,0);
        for(auto it :num)
        {
            ans-=(1ULL*(it.second-1)*it.second)>>1;
        }
        cout<<ans<<endl;
    }
}








  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值