zoukankan      html  css  js  c++  java
  • 哈希/图论-蔡老板分果子

    #238. 蔡老板分果子

     统计

    春天来了,万物复苏,动物们又到了发情的季节。蔡老板终于下定决心砍下了自家后院的两棵果树,并决定和自己喜欢的人一起分享果树上的果子。

    这两棵果树一棵是长生果树另一棵是人参果树,两棵树上都有 nn 个果子,编号为 1n1∼n,并分别由 n1n−1 段树枝连接起来。 为了把果子分成两份,蔡老板决定再两棵树上各砍一刀,分别砍断一根树枝把两棵树上的果子各分成两个部分。之后,对于每一棵果树,蔡老板会选择 11 号果子所在的那一部分。显然这样分果子一共有 (n1)2(n−1)2 种分法,而蔡老板想知道,有多少种切割方法,使得蔡老师拿到的长生果和人参果具有相同的标号集合。

    输入格式

    第一行一个正整数 nn 表示两棵树的大小。

    接下来 n1n−1 行每行两个正整数表示长生果树上的边。

    接下来 n1n−1 行每行两个正整数表示人参果树上的边。

    输出格式

    输出一行一个正整数表示蔡老板关心的切割方法的数目。

    样例1

    input

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

    output

    2
    
    

    explanation

    第一种切割方法:第一棵树上切 (2,3)(2,3),第二棵树上切 (1,3)(1,3),这样蔡老师拿到的长生果和人参果集合都是 {1,2}{1,2};

    第二种切割方法:第一棵树上切 (3,4)(3,4),第二棵树上切 (3,4)(3,4),这样蔡老师拿到的长生果和人参果集合都是 {1,2,3}{1,2,3}。

    样例2

    input

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

    output

    3
    
    

    样例3

    见样例数据下载

    限制与规定

    对于全部数据 1n2×105.1≤n≤2×105.

    本题采用捆绑测试,只有通过一个子任务的全部测试点才可以获得该子任务的分数,子任务及限制如下表所示:

    子任务编号分值nn其他限制
    1 4 10≤10
    2 19 100≤100
    3 20 103≤103
    4 21 2×105≤2×105 保证所有点的度数不超过 22
    5 36 2×105≤2×105

    时间限制:1s1s

    空间限制:256MB256MB

    下载

    样例数据下载

     法1. 对子树哈希

    需要设计一个哈希函数与树的形态和父子关系无关。想到了 (ap+bp)%q 这类哈希函数,会被卡掉。正解是直接用 xor 哈希,然后提前把 [1, N] 映射到一个 unsigned long long 的整数。据说(也可以感性感受到)这样的好处是每一位的影响是独立的。也就是说,只有每一位都冲突,两个哈希函数才可能冲突。这样就可以过。代码里把幂数和和异或和作为两个参数搞了一个双哈,不过真正起作用的是异或和。

     1 #include <stdio.h>
     2 #include <algorithm>
     3 #include <vector>
     4 #include <stdlib.h>
     5 
     6 using namespace std;
     7 
     8 typedef unsigned long long ull;
     9 
    10 const int _N = 220000;
    11 const ull P = 1e9+7;
    12 
    13 ull hs[_N];
    14 vector<int> G[2][_N];
    15 int N;
    16 
    17 struct data {
    18     ull a, b;
    19     
    20     bool operator < (const data &tmp) const
    21     {
    22         return a == tmp.a ? b < tmp.b : a < tmp.a;
    23     }
    24     
    25     bool operator == (const data &tmp) const
    26     {
    27         return a == tmp.a && b == tmp.b;
    28     }
    29 } A[2][_N];
    30 
    31 ull Mont(ull a)
    32 {
    33     a %= P;
    34     ull t = 1, b = 131;
    35     while (b) {
    36         if (b & 1) t = t*a%P;
    37         b >>= 1, a = a*a%P;
    38     }
    39     return t;
    40 }
    41 
    42 void DFS(int id, int node, int dad)
    43 {
    44     A[id][node].a = hs[node], A[id][node].b = Mont(hs[node]);
    45     for (int i = G[id][node].size()-1; i >= 0; --i) {
    46         int v = G[id][node][i];
    47         if (v != dad) {
    48             DFS(id, v, node);
    49             A[id][node].a = A[id][node].a^A[id][v].a, A[id][node].b = (A[id][node].b+A[id][v].b)%P;
    50         }
    51     }
    52     return;
    53 }
    54 
    55 inline ull umul(int a, int b) { return (ull)a * b; }
    56 
    57 void Ins(int id, int x, int y)
    58 {
    59     G[id][x].push_back(y), G[id][y].push_back(x);
    60     return;
    61 }
    62 
    63 int main()
    64 {
    65     int i, j, id;
    66     srand(19260817);
    67     scanf("%d", &N);
    68     for (i = 1; i <= N; ++i) hs[i] = umul(umul(rand(), rand()), rand())+rand()%97;
    69     for (id = 0; id <= 1; ++id)
    70         for (i = 1; i < N; ++i) {
    71             int x, y;
    72             scanf("%d%d", &x, &y);
    73             Ins(id, x, y);
    74         }
    75     for (id = 0; id <= 1; ++id)
    76         DFS(id, 1, -1), sort(A[id]+1, A[id]+1+N);
    77     i = j = 1;
    78     int ans = 0;
    79     while (i <= N && j <= N) {
    80         if (A[0][i] == A[1][j]) ++ans, ++i;
    81         else if (A[0][i] < A[1][j]) ++i;
    82         else ++j;
    83     }
    84     printf("%d
    ", ans-1);
    85     return 0;
    86 }

    法2. 机智的 dfs 序

    设两棵树为 A, B 。对 A 树进行 DFS,记录每个节点的 dfn[i] 以及 size[i] 。然后对 B 树进行 DFS,记录每个节点对应的 dfn[i] 值的 max[i] 和 min[i] 以及 size2[i] 。则此时 B 某子树与 A 的某子树可以以及为答案贡献的充要条件是 size2[i] = size[i] 且 max[i]-min[i]+1 = size[i] 。思路大概就是,当这两个条件同时成立时,B 的子树中节点对应的 dfn[i] 值(注意此处的 dfn 是指在对 A 进行 DFS 得到的某个点的 dfn)一定构成了 [min[i], max[i]] 这段连续的区间。而 A 中对应子树的 dfn 编号也是这段连续区间(连续性显然……OrzOrz)。这个方法非常机智,而且好写!

     1 #include <stdio.h>
     2 #include <vector>
     3 #include <algorithm>
     4 
     5 using namespace std;
     6 
     7 const int _N = 220000;
     8 
     9 int A[_N], B[_N], dfn[_N], size[_N], ans, Time, s2[_N];
    10 vector<int> G[2][_N];
    11 
    12 void DFS2(int node, int dad)
    13 {
    14     A[node] = B[node] = dfn[node];
    15     s2[node] = 1;
    16     for (int i = G[1][node].size()-1; i >= 0; --i) {
    17         int v = G[1][node][i];
    18         if (v != dad) {
    19             DFS2(v, node);
    20             A[node] = min(A[node], A[v]), B[node] = max(B[node], B[v]);
    21             s2[node] += s2[v];
    22         }
    23     }
    24     if (B[node]-A[node]+1 == size[A[node]] && size[A[node]] == s2[node])
    25         ++ans;
    26     return;
    27 }
    28 
    29 void DFS(int node, int dad)
    30 {
    31     dfn[node] = ++Time;
    32     size[dfn[node]] = 1;
    33     for (int i = G[0][node].size()-1; i >= 0; --i) {
    34         int v = G[0][node][i];
    35         if (v != dad) {
    36             DFS(v, node);
    37             size[dfn[node]] += size[dfn[v]];
    38         }
    39     }
    40     return;
    41 }
    42 
    43 void Ins(int id, int x, int y)
    44 {
    45     G[id][x].push_back(y), G[id][y].push_back(x);
    46     return;
    47 }
    48 
    49 int main()
    50 {
    51     int N, i, id;
    52     scanf("%d", &N);
    53     for (id = 0; id <= 1; ++id)
    54         for (i = 1; i < N; ++i) {
    55             int x, y;
    56             scanf("%d%d", &x, &y);
    57             Ins(id, x, y);
    58         }
    59     DFS(1, -1);
    60     DFS2(1, -1);
    61     printf("%d
    ", ans-1);
    62     return 0;
    63 }
  • 相关阅读:
    腾讯云Serverless来部署Hexo博客
    通用导入
    Winform窗体圆角完美解决方案
    kali 解决签名失效问题
    CVE-2021-3156 漏洞复现 附带 提权exp
    kali在安装时遇到软件包安装不了的问题解决
    Asmgcs高级地面学习笔记
    继续教育 多开视频加速
    python直接打印列表
    window里GDAL读取中文Personal Geodatabase
  • 原文地址:https://www.cnblogs.com/ghcred/p/9392416.html
Copyright © 2011-2022 走看看