题目传送门

https://vjudge.net/problem/UVA-11540

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2535(原题页面)

题解

首先如果两棵树经过一定的旋转以后能够重合,我们就称它们同构

判断树的同构的常见方法是哈希。但是由于子树的先后顺序是有影响的,只有旋转以后相同才算等价,所以我的哈希方法是直接把所有孩子组成的序列倍长以后,用类似于字符串哈希的方法求出以每个孩子开始一圈的哈希值,取最小的作为当前这棵树的哈希。这样我们就可以轻易的判断两棵子树等不等价了。

然后,我们令 \(dp[x]\) 表示 \(x\) 的子树内,本质不同的染色方案的数量。

根据 Burnside 引理,在求 \(x\) 的方案的时候,我们可以枚举它的孩子们的环的旋转的幅度,然后判断这样旋转合不合法,即——旋转以后对应的两棵子树必须同构。这样,对于一个合法的幅度,这个环一定被分成了一些组,每一组的每棵子树必须使用一样的染色方案。我们把这样的每一组的方案乘起来以后的结果取平均就可以了。

另外,上面是 \(O(n^2)\) 的,实际上可以用 kmp 的最小循环节来线性的求,不详细展开,可以自己研究。


下面是代码,如上文所述,dp 部分的时间复杂度为 \(O(n^2)\),建树为 \(O(n^2)\),因此总的时间复杂度为 \(O(Tn^2)\)。

  1. #include<bits/stdc++.h>
  2. #define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
  3. #define dbg(...) fprintf(stderr, __VA_ARGS__)
  4. #define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
  5. #define fi first
  6. #define se second
  7. #define pb push_back
  8. template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
  9. template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}
  10. typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
  11. template<typename I>
  12. inline void read(I &x) {
  13. int f = 0, c;
  14. while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
  15. x = c & 15;
  16. while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
  17. f ? x = -x : 0;
  18. }
  19. const int N = 100 + 7;
  20. const int P = 1e9 + 7;
  21. const int base = 1997;
  22. int n, k, nod;
  23. int siz[N], dp[N], vis[N];
  24. std::vector<int> g[N];
  25. char s[N * 10];
  26. ull h[N], tmp[N << 1], bin[N];
  27. inline int smod(int x) { return x >= P ? x - P : x; }
  28. inline void sadd(int &x, int y) { x += y; x >= P ? x -= P : x; }
  29. inline int fpow(int x, int y) {
  30. int ans = 1;
  31. for (; y; y >>= 1, x = (ll)x * x % P) if (y & 1) ans = (ll)ans * x % P;
  32. return ans;
  33. }
  34. inline int build(int l, int r) {
  35. // dbg("l = %d, r = %d\n", l, r);
  36. if (r < l) return ++nod;
  37. int o = ++nod, pre = l, cnt = 1;
  38. for (int i = l + 1; i <= r; ++i) {
  39. if (s[i] == '[') {
  40. if (!cnt) pre = i;
  41. ++cnt;
  42. } else if (s[i] == ']') {
  43. --cnt;
  44. if (!cnt) g[o].pb(build(pre + 1, i - 1));
  45. }
  46. }
  47. return o;
  48. }
  49. inline void dfs1(int x) {
  50. siz[x] = 1;
  51. int cnt = 0;
  52. for (int y : g[x]) {
  53. dfs1(y);
  54. siz[x] += siz[y], ++cnt;
  55. // tmp[tmp[0] + 1] = tmp[tmp[0]] * base + h[y], ++tmp[0];
  56. }
  57. int tt = 0;
  58. for (int y : g[x]) tmp[tt + 1] = tmp[tt] * base + h[y], ++tt;
  59. for (int y : g[x]) tmp[tt + 1] = tmp[tt] * base + h[y], ++tt;
  60. // dbg("****************** %llu %llu %llu %llu %llu\n", tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
  61. for (int i = cnt; i; --i) tmp[i] = tmp[i + cnt] - bin[cnt] * tmp[i];
  62. std::sort(tmp + 1, tmp + cnt + 1);
  63. // dbg("****************** %llu %llu %llu %llu %llu, %llu\n", tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], bin[cnt]);
  64. if (cnt >= 1) h[x] = tmp[1] * siz[x];
  65. else h[x] = 1;
  66. // dbg("******************\n");
  67. // dbg("h[%d] = %llu, siz = %d\n", x, h[x], siz[x]);
  68. }
  69. inline void dfs2(int x) {
  70. int cnt = 0, ms = 0;
  71. dp[x] = 0;
  72. for (int y : g[x]) dfs2(y), ++cnt;
  73. for (int stp = 0; stp < cnt; ++stp) {
  74. int flag = 1, s = 1;
  75. for (int i = 0; i < cnt && flag; ++i) if (!vis[i]) {
  76. for (int j = i; !vis[j] && flag; j += stp, j >= cnt ? j -= cnt : 0) {
  77. vis[j] = 1;
  78. if (h[g[x][i]] != h[g[x][j]]) flag = 0;
  79. }
  80. }
  81. for (int i = 0; i < cnt; ++i) vis[i] = 0;
  82. if (!flag) continue;
  83. for (int i = 0; i < cnt; ++i) if (!vis[i]) {
  84. for (int j = i; !vis[j]; j += stp, j >= cnt ? j -= cnt : 0) vis[j] = 1;
  85. s = (ll)s * dp[g[x][i]] % P;
  86. }
  87. for (int i = 0; i < cnt; ++i) vis[i] = 0;
  88. sadd(dp[x], s), ++ms;
  89. }
  90. // dbg("dp[%d] = %d, ms = %d\n", x, dp[x], ms);
  91. dp[x] = (ll)dp[x] * fpow(ms, P - 2) % P * k % P;
  92. if (!cnt) dp[x] = k;
  93. // dbg("dp[%d] = %d, ms = %d\n", x, dp[x], ms);
  94. }
  95. inline void vec_cls(std::vector<int> &x) {
  96. std::vector<int> y;
  97. std::swap(x, y);
  98. }
  99. inline void ycl() {
  100. bin[0] = 1;
  101. for (int i = 1; i <= n; ++i) bin[i] = bin[i - 1] * base;
  102. }
  103. inline void work() {
  104. nod = -1;
  105. for (int i = 1; i <= n; ++i) vec_cls(g[i]);
  106. build(1, strlen(s + 1));
  107. n = nod;
  108. ycl();
  109. int ans = 1;
  110. for (int i = 1; i <= n; ++i) if (!siz[i]) {
  111. dfs1(i);
  112. dfs2(i);
  113. ans = (ll)ans * dp[i] % P;
  114. }
  115. static int CS = 0;
  116. printf("Case #%d: %d\n", ++CS, ans);
  117. }
  118. inline void init() {
  119. memset(siz, 0, sizeof(siz));
  120. scanf("%s", s + 1);
  121. read(k);
  122. }
  123. int main() {
  124. #ifdef hzhkk
  125. freopen("hkk.in", "r", stdin);
  126. #endif
  127. int T;
  128. read(T);
  129. while (T--) {
  130. init();
  131. work();
  132. }
  133. fclose(stdin), fclose(stdout);
  134. return 0;
  135. }

UVA11540 Sultan's Chandelier Burnside 引理 + DP的更多相关文章

  1. BZOJ 1004 Cards(Burnside引理+DP)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1004 题意:三种颜色的扑克牌各有Sr,Sb,Sg张.给出m种置换.两种染色方案在某种置换 ...

  2. [BZOJ 1004] [HNOI2008] Cards 【Burnside引理 + DP】

    题目链接:BZOJ - 1004 题目分析 首先,几个定义和定理引理: 群:G是一个集合,*是定义在这个集合上的一个运算. 如果满足以下性质,那么(G, *)是一个群. 1)封闭性,对于任意 a, b ...

  3. BZOJ 1004 Cards(Burnside引理+DP)

    因为有着色数的限制,故使用Burnside引理. 添加一个元置换(1,2,,,n)形成m+1种置换,对于每个置换求出循环节的个数, 每个循环节的长度. 则ans=sigma(f(i))/(m+1) % ...

  4. bzoj1004: [HNOI2008]Cards(burnside引理+DP)

    题目大意:3种颜色,每种染si个,有m个置换,求所有本质不同的染色方案数. 置换群的burnside引理,还有个Pólya过几天再看看... burnside引理:有m个置换k种颜色,所有本质不同的染 ...

  5. bzoj 1004 burnside 引理+DP

    对于burnside引理需要枚举染色,这道题属于burnside的一种简单求解的方法,就是polya,我们可以使每一种置换中的循环节中的元素的颜色都相同,那么这样的话就可以直接DP了,我们可以将m个置 ...

  6. [BZOJ1547]周末晚会:Burnside引理+DP

    分析 Attention!这道题的模数是\(1e8+7\). 注意到循环同构会被认为是同一种方案,我们可以把顺时针旋转每个人的位置作为置换,容易发现这些置换一定会形成一个置换群,于是题目所求的所有合法 ...

  7. [bzoj 1004][HNOI 2008]Cards(Burnside引理+DP)

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1004 分析: 1.确定方向:肯定是组合数学问题,不是Polya就是Burnside,然后题目上 ...

  8. BZOJ 1004: [HNOI2008]Cards( 置换群 + burnside引理 + 背包dp + 乘法逆元 )

    题意保证了是一个置换群. 根据burnside引理, 答案为Σc(f) / (M+1). c(f)表示置换f的不动点数, 而题目限制了颜色的数量, 所以还得满足题目, 用背包dp来计算.dp(x,i, ...

  9. 【BZOJ1004】【HNOI2008】Cards 群论 置换 burnside引理 背包DP

    题目描述 有\(n\)张卡牌,要求你给这些卡牌染上RGB三种颜色,\(r\)张红色,\(g\)张绿色,\(b\)张蓝色. 还有\(m\)种洗牌方法,每种洗牌方法是一种置换.保证任意多次洗牌都可用这\( ...

随机推荐

  1. Flueme学习之路(一)Flume的基础介绍

    背景 Hadoop业务的整体开发流程: ​ 从Hadoop的业务开发流程中可以看出,在大数据的业务处理流程中,对于数据的采集是十分重要的一步,也是不可避免的一步. ​ 许多公司的平台每天会产生大量的日 ...

  2. Flask框架视图多层装饰器问题

    Flask中的app.route装饰器 我们知道,在flask框架中,我们的路由匹配就是通过有参装饰器来实现的,我们看一个简单的例子: from flask import Flask, render_ ...

  3. jmeter3.0+ant1.10+jenkins实现接口自动化并发送邮件

    有很多关于接口自动化的文章,此篇仅用于记录自己的学习用.使用jmeter3.0+ant1.10+jenkins2.实现接口自动化并发送邮件,本篇是用的编写build文件来实现发送邮件,也可以用jenk ...

  4. 把Notepad++的tab设置为四个空格

    在7.1版本以及以后 设置->首选项->Language 勾选Repalce by space 在7.1版本以前 设置->首选项->制表符设置 右侧,转为空格,勾选上 参考: ...

  5. mysql行锁和死锁检测

    行锁顾名思义,就是针对单行数据加锁,在mysql中,锁的实现是由引擎层实现的,MyISAM引擎就不支持行锁 不支持行锁就意味着并发控制只能使用表锁,也就是说同一时间,在这个表上只能有一个更新在执行,这 ...

  6. java--反射原理及操作

    1.反射原理 反射具体操作 15.反射的原理(********理解********) * 应用在一些通用性比较高的代码 中 * 后面学到的框架,大多数都是使用反射来实现的 * 在框架开发中,都是基于配 ...

  7. jQuery基础--CSS操作、class操作、attr操作、prop操作

    1.1.1    css操作 功能:设置或者修改样式,操作的是style属性. 设置单个样式 //name:需要设置的样式名称 //value:对应的样式值 css(name, value); //使 ...

  8. [Linux] 005 Linux 常见目录的作用及一些注意事项

    1. Linux 常见目录及其作用 目录名 作用 /bin/ 存放系统命令的目录普通用户各超级用户都可以执行放在 /bin 下的命令在单用户模式下也可以执行 /sbin/ 保存和系统环境相关的命令只有 ...

  9. [BZOJ4151]The Cave

    Solution: ​ 假设现在在点1,有许多形如 (x, y, z) 的限制条件,那么对于一组限制,必须先走到 x, y 的 \(\frac{z-dis(x, y)}{2}\) 级祖先,叫这些点为限 ...

  10. 两数相加(java版本)

    (一).单链表实现 package com.lin.leetcode.addTwoNumbers; /** * Created by Yaooo on 2019/8/26. */ public cla ...