首页 > 其他 > 详细

[BZOJ5361][Lydsy1805月赛]对称数

时间:2018-05-27 23:32:43      阅读:325      评论:0      收藏:0      [点我收藏+]

[BZOJ5361][Lydsy1805月赛]对称数

试题描述

小 Q 认为,偶数具有对称美,而奇数则没有。

给定一棵 \(n\) 个点的树,任意两点之间有且仅有一条直接或间接路径。这些点编号依次为 \(1\)\(n\),其中编号为 \(i\) 的点上有一个正整数 \(a_i\)

定义集合 \(S(u, v)\)\(u\) 点到 \(v\) 点的唯一最短路径上经过的所有点 \(x\)(包括 \(u\)\(v\)) 对应的正整数 \(a_x\) 的集合。小 Q 将在 \(m\)\(S(u, v)\) 中寻找最小的对称数。因为偶数具有对称美,所以对称数是指那些出现了偶数次 (包括 \(0\) 次) 的正整数。

请写一个程序,帮助小 Q 找到最小的对称数。

输入

第一行包含一个正整数 \(T(1 \le T \le 10)\),表示测试数据的组数。

每组数据第一行包含两个正整数 \(n, m(1 \le n, m \le 200000)\),分别表示点数和询问数。

第二行包含 \(n\) 个正整数 \(a_1, a_2, \cdots, a_n(1 \le a_i \le 200000)\),依次表示每个点上的数字。

接下来 \(n - 1\) 行,每行两个正整数 \(u_i, v_i(1 ≤ u_i, v_i \le n, u_i \ne v_i)\),表示一条连接 \(u_i\)\(v_i\) 的双向树边。

接下来 \(m\) 行,每行两个正整数 \(u_i, v_i(1 \le u_i, v_i \le n)\),依次表示每个询问。

输出

对于每个询问输出一行一个正整数,即最小的对称数。

输入示例

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

输出示例

2
1
1

数据规模及约定

见“输入

题解

偶然间发现一道水题,发现很可切,就顺手切了一下。

这题一眼树上莫队([捂脸]然后就一脸的不想做),但是看一下 \(12\texttt{s}\) 十组数据肯定不是这个复杂度啊,肯定有其他办法……

于是有想了什么询问离线然后点分治……后来发现这样不太可做。

想了一会,突然发现这不是不久前做过的一道题吗?

奇偶性,自然想到用 \(01\) 串表示,合并信息的时候直接异或,既然异或了,不妨直接维护每个节点到根节点这条链上的颜色的奇偶情况……然后这题解法就出来了。

用主席树维护每个节点到根节点上的所有颜色奇偶性组成的 \(01\) 串的区间哈希值。询问就是查询两个 \(01\) 串异或之后的第一个 \(0\) 的位置,直接在线段树上二分即可(就是看两个串的前缀异或起来是否等于全 \(1\) 串的哈希值)。这里用异或哈希,就是给每种颜色随机一种 unsigned long long 的哈希值,然后 \(01\) 串的哈希值就是所有 \(1\) 的位置对应的颜色的哈希值异或起来。

注意这里是点权,所以 lca 那里得再异或一下补上。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 200010
#define maxm 400010
#define maxlog 18
#define UL unsigned long long

UL hval[maxn], hpre[maxn];
UL randUL() {
    UL x = 0, t = (1 << 8) - 1;
    rep(i, 1, 8) x = (x << 8) | (rand() & t);
    return x;
}

int n, m, head[maxn], nxt[maxm], to[maxm], col[maxn];

void AddEdge(int a, int b) {
    to[++m] = b; nxt[m] = head[a]; head[a] = m;
    swap(a, b);
    to[++m] = b; nxt[m] = head[a]; head[a] = m;
    return ;
}

int M;
namespace Sgt {
    const int maxnode = 7200010;
    
    int ToT, lc[maxnode], rc[maxnode];
    UL hsum[maxnode];
    
    void clear() {
        memset(lc, 0, sizeof(int) * (ToT + 1));
        memset(rc, 0, sizeof(int) * (ToT + 1));
        memset(hsum, 0, sizeof(UL) * (ToT + 1));
        ToT = 0;
        return ;
    }
    
    void update(int &y, int x, int l, int r, int p, UL v) {
        hsum[y = ++ToT] = hsum[x] ^ v;
        if(l == r) return ;
        int mid = l + r >> 1; lc[y] = lc[x]; rc[y] = rc[x];
        if(p <= mid) update(lc[y], lc[x], l, mid, p, v);
        else update(rc[y], rc[x], mid + 1, r, p, v);
        return ;
    }
    
    int Query(int u, int v, int c) {
        int pos = col[c], l = 1, r = M;
        while(l < r) {
            int mid = l + r >> 1;
            if((hpre[mid] ^ hpre[l-1]) == (hsum[lc[u]] ^ hsum[lc[v]] ^ (l <= pos && pos <= mid) * hval[pos])) l = mid + 1, u = rc[u], v = rc[v];
            else r = mid, u = lc[u], v = lc[v];
        }
        if((hpre[l] ^ hpre[l-1]) == (hsum[u] ^ hsum[v] ^ (l == pos) * hval[pos])) l++;
        return l;
    }
}

int rt[maxn], fa[maxn][maxlog], dep[maxn];
void build(int u) {
    rep(i, 1, maxlog - 1) fa[u][i] = fa[fa[u][i-1]][i-1];
    rt[u] = rt[fa[u][0]]; Sgt::update(rt[u], rt[u], 1, M, col[u], hval[col[u]]);
    for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u][0]) fa[to[e]][0] = u, dep[to[e]] = dep[u] + 1, build(to[e]);
    return ;
}
int lca(int a, int b) {
    if(dep[a] < dep[b]) swap(a, b);
    dwn(i, maxlog - 1, 0) if(dep[a] - (1 << i) >= dep[b]) a = fa[a][i];
    dwn(i, maxlog - 1, 0) if(fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
    return a == b ? a : fa[b][0];
}

void work() {
    n = read();
    m = 0; memset(head, 0, sizeof(head));
    int q = read(); M = 0;
    rep(i, 1, n) col[i] = read(), M = max(M, col[i]);
    rep(i, 1, M) hval[i] = randUL(), hpre[i] = hpre[i-1] ^ hval[i];
    rep(i, 1, n - 1) {
        int a = read(), b = read();
        AddEdge(a, b);
    }
    Sgt::clear(); memset(rt, 0, sizeof(rt));
    build(1);
    while(q--) {
        int u = read(), v = read();
        printf("%d\n", Sgt::Query(rt[u], rt[v], lca(u, v)));
    }
    return ;
}

int main() {
    int T = read();
    
    while(T--) work();
    
    return 0;
}

[BZOJ5361][Lydsy1805月赛]对称数

原文:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/9097771.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!