Beauty of Trees 2018华中科技大学程序设计竞赛决赛同步赛A

It’s universally acknowledged that there’re innumerable trees in the campus of HUST.

One day the tree manager wants to play a game with you. There are N trees lining up in a straight road. The beauty of a set of trees is defined as the bitwise XOR sum of the heights of these trees. The game will last for M rounds, and each time he will tell you an interval [Li,Ri] and its beauty. However, he mixed some fake messages with the correct ones. Your task is to find the messages that cannot logically correspond to its former correct messages. Otherwise you’ll think the message is correct.

The first line contains two integer N and M(1≤N,M≤105), the number of trees and the rounds of game.
Then M lines followed, in each line are three integer L, R and k(1≤L≤R≤N,0≤k≤109), 
indicating that the beauty of [Li,Ri] is k.

3 4
1 3 6
1 2 5
3 3 10000
3 3 3
3
加权并查集:
/**
先要知道加权并查集:https://blog.csdn.net/qq_32944513/article/details/80218138
然后 这里
设 P[i] 为 0~i 的异或值, 则给了 (L,R)=k 相当就是知道了 P[r]^P[l-1]=k;
这样转化一下 就和 LA4487 差不多了。
**/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long int ll;
const int maxn = 100000;
const int inf = 1e9;
int n,m,fa[maxn+5],r[maxn+5],ans[maxn+5];
int findset(int x)
{
    if(fa[x] == x) return x;
    int fx = fa[x];
    fa[x] = findset(fa[x]);
    r[x]^=r[fx];
    return fa[x];
}
int Merge(int u,int v,int z)
{
    int fx = findset(u), fy = findset(v);
    if(fx==fy)
    {
        return (r[u]^r[v])==z;
    }
    fa[fx] = fy;
    r[fx]=r[u]^r[v]^z;
    return true;
}
int main()
{
    int num = 0;
    scanf("%d %d",&n,&m);
    for(int i=0;i<=n;i++) fa[i] = i,r[i] = 0;
    for(int i=1; i<=m; i++)
    {
        int l,r,w;
        scanf("%d %d %d",&l,&r,&w);
        if(!Merge(l-1,r,w)) ans[++num] = i;
    }
    for(int i=1;i<=num;i++) printf("%d\n",ans[i]);
    if(num==0) printf("-1\n");
    return 0;
}

官方题解 没事看一看 思路很好:

/*
考虑给定的区间[L,R]中的数和美丽值k的二进制分解。对于第j位,如果k在这一位是1,
说明[L,R]之间在这一位为1的数有奇数个,接下来令f[i][j]表示第j位为1的数在前i个数里有多少个,
那么这个条件也就是f[l-1][j]与f[r][[j]奇偶性不同;若k在这一位为0,
说明f[l-1][j]与f[r][j]奇偶性相同。注意上述条件都是等价的,
也就是说如果所有的j位f[l-1][j]和先前确定的与f[r][j]的关系不矛盾的话,
就等价于题目所给的合乎逻辑。这个过程可以用并查集来完成。
对于每一个前缀和开一个实点一个虚点,如果两个前缀和奇偶性相同,
就实对实虚对虚相连;如果奇偶性不同,就各自实对虚相连。
这样就能保证与任意节点奇偶性偶数次不同相当于奇偶性相同。然后判断的时候可以直接查询。
*/
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

const int maxn=1e5+10;

int n,m,l,r,k,fl,cnt;
int f[maxn*2][32];

int find(int t,int k)
{
    int p=t,r;
    while(t!=f[t][k])t=f[t][k];
    while(p!=t)r=f[p][k],f[p][k]=t,p=r;
    return t;
}

void unio(int l,int r,int j)
{
    if(find(l,j)!=find(r,j))
        f[find(l,j)][j]=find(r,j);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0; i<=n*2+1; i++)
        for(int j=0; j<=30; j++)
            f[i][j]=i;

    for(int i=1; i<=m; i++)
    {
        fl=0;
        scanf("%d%d%d",&l,&r,&k);
        l--;

        for(int j=0; j<=30; j++)
            if((k&(1LL<<j))!=0&&(find(l,j)==find(r,j)||find(l+n+1,j)==find(r+n+1,j)))
            {
                printf("%d\n",i);
                fl=1;
                break;
            }
            else if((k&(1LL<<j))==0&&(find(l,j)==find(n+r+1,j)||find(r,j)==find(n+l+1,j)))
            {
                printf("%d\n",i);
                fl=1;
                break;
            }
        if(fl)continue;

        cnt++;
        for(int j=0; j<=30; j++)
            if((k&(1LL<<j))!=0)unio(l,r+n+1,j),unio(r,l+n+1,j);
            else unio(l,r,j),unio(l+n+1,r+n+1,j);
    }

    if(cnt==m)printf("-1");

    return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_32944513/article/details/80218638