2017年10月6日提高组T2 有趣的异或

Description


这里写图片描述

Input


这里写图片描述

Output


这里写图片描述

Hint


这里写图片描述

Solution


看到判断真假条件的都要想到并查集了(哭
但是这里我并没有用并查集做法

考虑我们如果已经得到了目标序列a,并记录一个前缀异或数组s,则对于全部的询问l, r, k都要满足s[r]xor s[l-1]=k。那么我们对于一个操作l, r, k,若l-1和r同属于一个集合就判断他们之间的路径是否为k,若他们不属于同一个集合就连权为k的边合并

这里在合并的时候有一个技巧就是小的并上大的,这样每次dfs小集合更改异或值的时候就总的复杂度就是NlogN了

对于每一个独立的集合,只要任意一个数字确定了剩下的数字就都确定了。那么每个集合内第一个遇到的数字填0就可以了。注意一下这里的0是序列a的值,并不是前缀异或和s

Code


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define drp(i, st, ed) for (int i = st; i >= ed; i -= 1)
#define erg(i, st) for (int i = ls[st]; i; i = e[i].next)
#define fill(x, t) memset(x, t, sizeof(x))
#define max(x, y) ((x)>(y)?(x):(y))
#define min(x, y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<(0)?(-(x)):(x))
#define N 400001
#define E N << 2 | 1
#define L 11
struct edge {int x, y, w, next;} e[E];
int used[N], size[N], dis[N], fa[N], ls[N], edgeCnt = 0;
inline int read() {
    int x = 0; char ch = getchar();
    for(; ch<'0'||ch>'9'; ch=getchar());
    for(; ch<='9'&&ch>='0'; (x*=10)+=ch-'0',ch=getchar());
    return x;
}
inline void writeln(int x){
    char ch[L] = {}; int i = 0;
    do {ch[++ i] = '0' + x % 10;} while (x /= 10);
    while (i) putchar(ch[i --]);
    putchar('\n');
}
inline void addEdge(int x, int y, int w) {
    e[++ edgeCnt] = (edge) {x, y, w, ls[x]}; ls[x] = edgeCnt;
    e[++ edgeCnt] = (edge) {y, x, w, ls[y]}; ls[y] = edgeCnt;
}
inline void swap(int &x, int &y) {
    x ^= y;
    y ^= x;
    x ^= y;
}
inline int getFather(int now) {
    return ((now == fa[now])?(now):(fa[now] = getFather(fa[now])));
}
inline int getDis(int x, int y) {
    return dis[x] ^ dis[y];
}
inline void dfs(int f, int now) {
    used[now] = 1;
    erg(i, now) {
        if (e[i].y == f) {
            continue;
        }
        dis[e[i].y] = dis[now] ^ e[i].w;
        dfs(now, e[i].y);
    }
}
int main(void) {
    int n = read() + 1;
    int m = read();
    int czy = read();
    int lastAns = 0;
    rep(i, 1, n) {
        fa[i] = i;
        size[i] = 1;
    }
    while (m --) {
        int l = read() ^ (czy * lastAns);
        int r = read() ^ (czy * lastAns);
        int k = read() ^ (czy * lastAns);
        r += 1;
        int fl = getFather(l);
        int fr = getFather(r);
        if (fl == fr) {
            lastAns = (k == getDis(l, r));
        } else {
            addEdge(l, r, k);
            if (size[fl] < size[fr]) {
                swap(l, r);
                swap(fl, fr);
            }
            size[fl] += size[fr];
            dis[r] = dis[l] ^ k;
            dfs(l, r);
            fa[fr] = fl;
            lastAns = 1;
        }
        writeln(lastAns);
    }
    fill(used, 0);
    rep(i, 1, n) {
        if (!used[i]) {
            dis[i] = dis[i - 1];
            dfs(0, i);
        }
    }
    rep(i, 2, n) {
        int prt = getDis(i, i - 1);
        writeln(prt);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值