Miscellaneous
*[2024.06.28] 珍珠
一个 \(n\) 个珠子的项链可翻转、可旋转,染 \(01\) 颜色,问本质不同的项链中,满足存在一段子串为给定 \(s\) ,这些项链的个数。
\(n\le 10^9,m=|s|,m\le 22\)。
考虑带权 Burnside 引理,转换成:
其中 \(W(S)=\sum_{c\in S}w(c)\),\(w(c)\) 为 \(1\) 当且仅当 \(c\) 满足题目要求,否则为 \(0\)。
对于后面的和式,我们可以将 \(G\) 考虑分为两部分。
记 \(\Delta=(2,3,\cdots,n,1),\rho = (n,n-1,\cdots, 1)\)。
会发现一部分是未反转的部分,记为 \(G_1=\{\Delta^t|t\in \N^*\}\)。
而另一部分是反转过的,记为 \(G_2=\{\Delta^t\circ \rho|t\in \N^*\}\)。
会发现有循环和反转在环上的性质显然有:\(G_1\cup G_2=G,G_1\cap G_2=\empty\)。
且因为 \(\Delta^n=\iota\),因此 \(|G_1|=|G_2|=n\)。
首先考虑 \(G_2\) 部分的 \(\sum W(C(g))\) 怎么算。
考虑 \(n\) 的奇偶性,如果 \(n\) 为奇数,就只有一种情况,否则会出现两种情况。这里我们讨论奇数,因为偶数的情况类似,只是更加麻烦不易理解。
考虑 \(n\) 为奇数的情况,对于一个 \(t\),\(\Delta^t\circ \rho=(t,t-1,\cdots,1,n,n-1,\cdots,t+1)\)。因此\(C(\Delta^t\circ \rho)\) 中的串一定都是由两个回文串拼接成的,但是如果我们再将 \(\Delta^{-t}\) 作用在这些串上,一定会一一对应 \(C(\rho)\) 中的串,因为置换有逆,因此这个作用可以看作 \(C(\Delta^t\circ \rho)\) 到 \(C(\rho)\) 的双射。
而且同时知道 \(w(c)=w(\Delta^{k}*c)\),结合双射可知有 \(W(C(\Delta^t\circ \rho))=W(C(\rho))\)。
因此可知 \(\forall f,g\in G_2,W(C(f))=W(C(g))\)。
所以我们只需要考虑 \(W(C(\rho))\) 也就是有多少个长度为 \(n\) 的回文串满足首尾拼接成环后出现 \(s\) 或 \(s^R\)。正着不太好做,考虑反着算,即计算有多少串不满足条件。
考虑记扫描字符串的状态 \((x,y)\) 表示以当前顺序扫描到的字符串,\(x\) 为最长的后缀长度满足这个后缀是 \(s\) 的前缀,\(y\) 为最长的后缀长度满足这个后缀是 \(s^R\) 的前缀。会发现这些状态只有 \(O(m)\) 个,可以通过 AC 自动机或者 KMP 自动机维护。
因此可以先 \(O(2^m)\) 枚举前面的串,判断首位拼接是否合法并记录到状态各自的初始方案数,随后进行矩阵乘法,然后再在中心判断是否出现 \(s,s^R\) ,如果没有出现,即可计入答案。
这部分时间复杂度为 \(O(m^3\log_2 n+m2^m)\)。
如果要考虑 \(n\) 为偶数则还需考虑一种情况,就是两个奇长度的回文串的拼接,这种情况只能转换成一个单字符再加上一个奇回文串的情况,和上面的计算方法同理。
随后是 \(G_1\) 部分,这部分是简单的。
存在三个性质,强度是递进的:
- \(\Delta^t\) 中有 \(\gcd (t, n)\) 个置换环。
- \(\Delta^t\) 中 \(i\) 和 \(i+\gcd (t,n)\) (如果存在)一定属于一个轮换。
- \(\Delta^t\) 中 \(\forall 1\le i\le \gcd (t,n)\) 归属的轮换各不相同。
证明是简单的初等数论知识。
拥有这三个性质,我们知道对于 \(\Delta^t\) 考虑 \(W(C(\Delta^t))\) 只需要考虑长度为 \(\gcd(t,n)\) 并满足首位拼接后出现 \(s\) 或 \(s^R\),统计这样的串的个数即为 \(W(C(\Delta^t))\)。
所以考虑 \(F(x)\) 表示长度为 \(x\) 并满足首位拼接后出现 \(s\) 或 \(s^R\) 的串的个数。
\(F(x)\) 可以用矩阵乘法计算。同样先计算出从头到尾没出现过 \(s\) 或 \(s^R\) 的串的个数再做减法,我们可以先钦定末尾的状态是什么,做完矩阵乘法后再取出同样状态位置上的数计入答案即可。
那么知道有:
时间复杂度 \(O(d(n)m^3\log_2 n)\)。
总的时间复杂度是 \(O(d(n)m^3\log_2n+m2^m)\)。
// Not afraid to dark.#include <bits/stdc++.h>
using namespace std;clock_t start_time, end_time;
#define GET_START start_time = clock ();
#define GET_END end_time = clock (); fprintf (stderr, "TIME COSSEMED : %0.3lf\n", 1.0 * (end_time - start_time) / CLOCKS_PER_SEC);
#define inline __inline__ __attribute__ ((always_inline))#define int long longnamespace io {int read_pos, read_dt; char read_char;inline int read (int &p = read_pos){p = 0, read_dt = 1; read_char = getchar ();while (! isdigit (read_char)){if (read_char == '-')read_dt = - 1;read_char = getchar ();}while (isdigit (read_char)){p = (p << 1) + (p << 3) + read_char - 48;read_char = getchar ();}return p = p * read_dt;}int write_sta[65], write_top;inline void write (int x){if (x < 0)putchar ('-'), x = - x;write_top = 0;dowrite_sta[write_top ++] = x % 10, x /= 10;while (x);while (write_top)putchar (write_sta[-- write_top] + 48);}int llen;inline int get_string (char c[], int &len = llen){len = 0;read_char = getchar ();while (read_char == ' ' || read_char == '\n' || read_char == '\r')read_char = getchar ();while (read_char != ' ' && read_char != '\n' && read_char != '\r'){c[++ len] = read_char;read_char = getchar ();}return len;}
}template < int SIZE >
struct Automaton {int nex[SIZE + 5][2], len[SIZE + 5], tot;int a[SIZE + 5], cnt;inline void init (){nex[0][0] = nex[0][1] = len[0] = 0;tot = cnt = 0;}inline int new_node (){++ tot;nex[tot][0] = nex[tot][1] = len[tot] = 0;return tot;}inline void Insert (int x){static int u, fw;a[++ cnt] = x;u = tot;fw = nex[u][x];nex[u][x] = new_node ();nex[tot][0] = nex[fw][0];nex[tot][1] = nex[fw][1];}inline int to (int x, int c){return nex[x][c];}
};const int M = 22, mod = 1e9 + 7;int n, m, l, ans;
char s[M + 5], t[M + 5];Automaton < M + 1 > pre, suf;int cn;
vector < pair < int , int > > state;
map < pair < int , int > , int > st;struct matrix {int a[M << 1 | 1][M << 1 | 1];inline void init (int c = 0){for (int i = 0;i < cn;++ i)for (int j = 0;j < cn;++ j)a[i][j] = (i == j ? c : 0);}inline matrix friend operator * (const matrix a, const matrix b){static matrix res;static int i, j, k;static __int128 x;res.init ();for (i = 0;i < cn;++ i)for (j = 0;j < cn;++ j){for (k = 0, x = 0;k < cn;++ k)x += (__int128) a.a[i][k] * b.a[k][j];res.a[i][j] = x % mod;}return res;}matrix (int c = 0){init (c);}
};inline matrix power (matrix a, int p){matrix res (1);while (p > 0){if (p & 1)res = res * a;a = a * a;p >>= 1;}return res;
}
inline int power (int a, int p){int res = 1;while (p > 0){if (p & 1)res = res * a % mod;a = a * a % mod;p >>= 1;}return res;
}inline int nex (int x, int c){static int p, q;tie (p, q) = state[x];p = pre.to (p, c);q = suf.to (q, c);return st[make_pair (p, q)];
}
inline bool tar (int x){return state[x].first == m || state[x].second == m;
}inline void Calc_Reverse (){matrix res, g;int rt = 0;auto od_calc = [&] (){int T = 0;for (int i = 0, pt, j, u, v;i < cn;++ i)if (res.a[0][i]){u = i;for (pt = 0;pt < 2;++ pt){v = nex (u, pt);if (tar (v))continue;if (state[i].first >= state[i].second)for (j = state[i].first;j >= 1;-- j){v = nex (v, s[j] - '0');if (tar (v))goto E_1;}elsefor (j = state[i].second - 1;j >= 0;-- j){v = nex (v, s[m - j] - '0');if (tar (v))goto E_1;}rt = (rt + res.a[0][i]) % mod;T += res.a[0][i];E_1 : ;}}};for (int i = 0, c;i < cn;++ i)for (c = 0;c < 2;++ c)if (! tar (i) && ! tar (nex (i, c)))g.a[i][nex (i, c)] = 1;if (n & 1){int t = min (m, n >> 1);for (int i = 0, j, u, v;i < (1 << t);++ i){for (j = t - 1, v = 0;j >= 0;-- j){v = nex (v, i >> j & 1);if (tar (v))goto E_2;}for (j = u = 0;j < t;++ j){u = nex (u, i >> j & 1);v = nex (v, i >> j & 1);if (tar (v))goto E_2;}++ res.a[0][u];E_2 : ;}res = res * power (g, (n >> 1) - t);od_calc ();rt = (power (2, (n + 1) >> 1) - rt + mod) * n % mod;} else {int t = min (m, n >> 1);for (int i = 0, j, u, v;i < (1 << t);++ i){for (j = t - 1, v = 0;j >= 0;-- j){v = nex (v, i >> j & 1);if (tar (v))goto E_3;}for (j = u = 0;j < t;++ j){u = nex (u, i >> j & 1);v = nex (v, i >> j & 1);if (tar (v))goto E_3;}++ res.a[0][u];E_3 : ;}res = res * power (g, (n >> 1) - t);for (int i = 0, j, u;i < cn;++ i)if (res.a[0][i]){u = i;if (state[i].first >= state[i].second){for (j = state[i].first;j >= 1;-- j){u = nex (u, s[j] - '0');if (tar (u))goto E_4;}} else{for (j = state[i].second - 1;j >= 0;-- j){u = nex (u, s[m - j] - '0');if (tar (u))goto E_4;}}rt = (rt + res.a[0][i] % mod) % mod;E_4 : ;}res.init ();t = min (m, (n >> 1) - 1);for (int i = 0, j, u, v, s, pt;i < (1 << t);++ i){for (j = t - 1, u = 0;j >= 0;-- j){u = nex (u, i >> j & 1);if (tar (u))goto E_5;}for (pt = 0;pt < 2;++ pt){v = nex (u, pt);if (tar (v))goto E_6;for (j = s = 0;j < t;++ j){s = nex (s, i >> j & 1);v = nex (v, i >> j & 1);if (tar (v))goto E_6;}++ res.a[0][s];E_6 : ;}E_5 : ;}res = res * power (g, (n >> 1) - 1 - t);od_calc ();rt = (power (2, n >> 1) + power (2, (n >> 1) + 1) - rt + mod) * (n >> 1) % mod;}ans = (ans + rt) % mod;
}
inline void Calc_inord (){vector < int > fac;int r = n;for (int i = 2;i * i <= r;++ i)while (r % i == 0){if (fac.empty () || fac.back () != i)fac.push_back (i);r /= i;}if (r > 1)fac.push_back (r);auto phi = [&] (const int &x){int res = x;for (int y : fac)if (x % y == 0)res /= y, res *= (y - 1);return res;};matrix A, g;for (int i = 0, c;i < cn;++ i)for (c = 0;c < 2;++ c)if (! tar (i) && ! tar (nex (i, c)))g.a[i][nex (i, c)] = 1;auto F = [&] (const int x){int res = 0;A.init ();for (int i = 0;i < cn;++ i){if (max (state[i].first, state[i].second) == m)continue;A.a[i][i] = 1;}A = A * power (g, x);for (int i = 0;i < cn;++ i)res = (res + A.a[i][i]) % mod;return (power (2, x) - res + mod) % mod;};int rt = 0;for (int i = 1;i * i <= n;++ i)if (n % i == 0){rt = (rt + phi (n / i) * F (i) % mod) % mod;if (n / i != i)rt = (rt + phi (i) * F (n / i) % mod) % mod;}ans = (ans + rt) % mod;
}signed main (){GET_STARTio::read (n), io::read (m);io::get_string (s);if (n == 1)return putchar ('1'), putchar ('\n'), 0;if (n == 2){if (m == 2)return putchar ('1'), putchar ('\n'), 0;elsereturn putchar ('2'), putchar ('\n'), 0;}pre.init ();for (int i = 1;i <= m;++ i)pre.Insert (s[i] - '0'), suf.Insert (s[m - i + 1] - '0');state.push_back (make_pair (0, 0));for (int i = 1, j, u;i <= m;++ i){for (j = 1, u = 0;j <= i;++ j)u = suf.to (u, s[j] - '0');state.push_back (make_pair (i, u));for (j = m, u = 0;j > m - i;-- j)u = pre.to (u, s[j] - '0');state.push_back (make_pair (u, i));}sort (state.begin (), state.end ());state.erase (unique (state.begin (), state.end ()), state.end ());cn = (int) state.size ();for (int i = 0;i < cn;++ i)st[state[i]] = i;Calc_Reverse ();Calc_inord ();io::write (ans * power ((n << 1) % mod, mod - 2) % mod), putchar ('\n');GET_ENDreturn 0;
}
Codeforces 1987G1 - Spinning Round (Easy Version)
考虑对于以 \(1\) 为根的情况,一定可以分成两段不交的路径,分别线段树维护单调栈扫前后缀即可。
但是需要注意的是不以 \(1\) 为根的情况,这时发现对于一段前缀,如果最终不以 \(1\) 为根且要以自身为一段路径,一定会以后面第一个大于当前后缀最大值的点为根,对于每个前缀这个点是唯一的,因此扫后缀是顺便记录即可。扫前缀时同理。
Submission #268435009 - Codeforces
Codeforces 1987G2 - Spinning Round (Hard Version)
不再考虑复杂的线段树。
建出笛卡尔树,发现如果不是连向笛卡尔树上的父亲,一定就会连向从它往上的祖先中,第一个改变指向的点的父亲。
于是考虑 DP 方程,记 \(f(x,0/1/2)\) 表示 \(x\) 子树中最终从左侧跳出的最长路径、最终从右侧跳出的最长路径、分别从左右两边跳出的两条路径和的最大值。
转移是简单的。
Submission #268861885 - Codeforces