Educational Codeforces Round 40 (Rated for Div. 2) Solution
从这里开始
Problem A Diagonal Walking
题目大意
给定一个只包含'U'和'R'的字符串,你可以将"RU"或者'UR"替换为"D"。问能使串长能变成的最小值。
直接把 RU 或者 UR 替换为 D 即可。
Code
/** * Codeforces * Problem#954A * Accepted * Time: 31ms * Memory: 0k */ #include <bits/stdc++.h> using namespace std; typedef bool boolean; int n; char s[233]; int main() { scanf("%d", &n); scanf("%s", s + 1); int ans = n; for (int i = 1; i < n; i++) { if (s[i] ^ s[i + 1]) { i++, ans--; continue; } } printf("%d\n", ans); return 0; }
Problem B String Typing
题目大意
有一台打字机,要输入1个字符串,有两种操作:
- 向末尾输入一个字符
- 将当前输入的字符串复制一遍粘在后面
操作2只能使用1次。问最少的操作次数。
没看到只能用1次,傻傻地写了个dp。并成功获得了:
然后急急忙忙去加个状态。
其实只能用1次直接贪心就好了。当减少的操作次数最多的时候用操作2.
Code
1 /** 2 * Codeforces 3 * Problem#954B 4 * Accepted 5 * Time: 31ms 6 * Memory: 0k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 int n; 13 char str[105]; 14 int f[105][2]; 15 16 inline void init() { 17 scanf("%d", &n); 18 scanf("%s", str + 1); 19 } 20 21 boolean equal(int l1, int l2, int len) { 22 for (int i = 0; i < len; i++) 23 if (str[l1 + i] != str[l2 + i]) 24 return false; 25 return true; 26 } 27 28 inline void solve() { 29 f[0][1] = 2000; 30 for (int i = 1; i <= n; i++) { 31 f[i][0] = f[i - 1][0] + 1; 32 f[i][1] = f[i - 1][1] + 1; 33 if (!(i & 1) && (equal(1, (i >> 1) + 1, i >> 1))) 34 f[i][1] = min(f[i][1], f[i >> 1][0] + 1); 35 } 36 printf("%d\n", min(f[n][0], f[n][1])); 37 } 38 39 int main() { 40 init(); 41 solve(); 42 return 0; 43 }
Problem C Matrix Walk
题目大意
有一个$x\times y$的网格图,但是不知道$x, y$的值。规定$i$行$j$列的格子的标号为$(i - 1)y + j$。
给定一个长度为$n$的经过的格子序列,规定只能走相邻的格子,且不能走出边界,也不能停留在同一个格子。问是否可能存在一组$x, y$使得路径合法,如果存在,输出这一组。
找最大的一组相邻的差作为$y$。
$x$足够大就好了(为什么?因为没影响)。
然后代进去检验是否合法。
Code
1 /** 2 * Codeforces 3 * Problem#954C 4 * Accepted 5 * Time: 61ms 6 * Memory: 800k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 int n; 13 int *ar; 14 15 inline void init() { 16 scanf("%d", &n); 17 ar = new int[(n + 1)]; 18 for (int i = 1; i <= n; i++) 19 scanf("%d", ar + i); 20 } 21 22 int mxd = 1; 23 set<int> s; 24 void termin() { 25 puts("NO"); 26 exit(0); 27 } 28 29 inline void solve() { 30 for (int i = 1; i < n; i++) { 31 int dif = abs(ar[i + 1] - ar[i]); 32 if (!dif) 33 termin(); 34 if (mxd > 1 && dif > 1 && dif != mxd) 35 termin(); 36 if (dif > 1) 37 mxd = dif; 38 } 39 40 int ly = ar[1] % mxd; 41 if (!ly) ly = mxd; 42 int lx = (ar[1] - ly) / mxd + 1; 43 for (int i = 2, cx, cy; i <= n; i++) { 44 cy = ar[i] % mxd; 45 if (!cy) cy = mxd; 46 cx = (ar[i] - cy) / mxd + 1; 47 if (abs(cx - lx) + abs(cy - ly) != 1) 48 termin(); 49 lx = cx, ly = cy; 50 } 51 puts("YES"); 52 printf("%d %d", 1000000000, mxd); 53 } 54 55 int main() { 56 init(); 57 solve(); 58 return 0; 59 }
Problem D Fight Against Traffic
题目大意
给定$n$个点$m$条边的无向连通简单图。每条边边权均为1。
要求加一条原本不存在的边,使得新图没有自环,$s, t$之间的距离不改变。
问方案数。
看这数据范围挺小的。
分别跑出$s, t$的最短路径生成树。
然后枚举边的两个端点,判断加入后新产生的路径长度是否合法。
Code
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef bool boolean; 4 5 const int N = 1e3 + 5; 6 7 int n, m, s, t; 8 boolean g[N][N]; 9 int f1[N], f2[N]; 10 11 inline void init() { 12 scanf("%d%d%d%d", &n, &m, &s, &t); 13 for (int i = 1, u, v; i <= m; i++) { 14 scanf("%d%d", &u, &v); 15 g[u][v] = true, g[v][u] = true; 16 } 17 } 18 19 typedef class Node { 20 public: 21 int p, dis; 22 23 Node(int p = 0, int dis = 0):p(p), dis(dis) { } 24 25 boolean operator < (Node b) const { 26 return dis > b.dis; 27 } 28 }Node; 29 30 priority_queue<Node> que; 31 void dijstra(int s, int* f) { 32 memset(f, 0x7f, sizeof(int) * (n + 1)); 33 que.push(Node(s, f[s] = 0)); 34 while (!que.empty()) { 35 Node e = que.top(); 36 que.pop(); 37 if (e.dis != f[e.p]) 38 continue; 39 for (int i = 1; i <= n; i++) 40 if (g[e.p][i] && e.dis + 1 < f[i]) 41 que.push(Node(i, f[i] = e.dis + 1)); 42 } 43 } 44 45 int ans = 0; 46 inline void solve() { 47 dijstra(s, f1); 48 dijstra(t, f2); 49 for (int i = 1; i <= n; i++) 50 for (int j = i + 1; j <= n; j++) 51 if (!g[i][j] && f1[i] + f2[j] + 1 >= f1[t] && f1[j] + f2[i] + 1 >= f1[t]) 52 ans++; 53 printf("%d", ans); 54 } 55 56 int main() { 57 init(); 58 solve(); 59 return 0; 60 }
Problem E Water Taps
题目大意
给定$n, T, a_{i}, t_{i}$,找出一组$x_{i}$满足$0\leqslant x_{i} \leqslant a_{i}$,且$\frac{\sum_{i = 1}^{n}x_{i}t_{i}}{\sum_{i = 1}^{n}x_{i}} = T$。问$\sum_{i = 1}^{n}x_{i}$的最大值。无解输出0。
移项,排序后贪心即可。
Code
/** * Codeforces * Problem#954E * Accepted * Time: 93ms * Memory: 4400k */ #include <bits/stdc++.h> using namespace std; typedef bool boolean; const int N = 2e5 + 5; #define ll long long #define pii pair<int, int> int n, T; ll sum = 0; int a[N]; vector<pii> vL, vR; int main() { scanf("%d%d", &n, &T); double ans = 0; for (int i = 1; i <= n; i++) { scanf("%d", a + i); ans += a[i]; } for (int i = 1, t; i <= n; i++) { scanf("%d", &t); sum += 1ll * a[i] * (t - T); if (t <= T) { vL.emplace_back(T - t, a[i]); } else { vR.emplace_back(t - T, a[i]); } } if (sum < 0) { sum = -sum; } else { vL = vR; } sort(vL.begin(), vL.end(), greater<pii>()); for (auto e : vL) { if (!sum) break; ll tmp = 1ll * e.first * e.second; if (tmp > sum) { ans -= 1.0 * sum / e.first; break; } else { sum -= tmp; ans -= e.second; } } printf("%.9lf\n", ans); return 0; }
Problem F Runner's Problem
题目大意
给定一个$3\times m$的棋盘,在$(2, 1)$的地方有一个棋子,棋子每次只能移动到下一列的相邻的行或者当前行。
棋盘上有$n$段障碍,第$i$段障碍是在第$a_{i}$行的$l_{i}$列到第$r_{i}$列。棋子不能到障碍格上。
问棋子从$(2, 1)$到$(2, m)$的方案数。(答案模$10^{9} + 7$)
随便写写dp方程,把转移表变成矩阵。跑快速幂。
Code
1 /** 2 * Codeforces 3 * Problem#954F 4 * Accepted 5 * Time: 390ms 6 * Memory: 1700k 7 */ 8 #include <bits/stdc++.h> 9 #ifndef WIN32 10 #define Auto "%lld" 11 #else 12 #define Auto "%I64d" 13 #endif 14 using namespace std; 15 typedef bool boolean; 16 17 #define ll long long 18 #define pli pair<ll, int> 19 20 typedef class Segment { 21 public: 22 ll l, r; 23 int sta; 24 25 Segment() { } 26 Segment(ll l, ll r, int sta):l(l), r(r), sta(sta) { } 27 }Segment; 28 29 const int M = 1e9 + 7; 30 31 typedef class Matrix { 32 public: 33 int r, c; 34 int a[3][3]; 35 36 Matrix(int r = 0, int c = 0):r(r), c(c) {} 37 38 Matrix operator * (Matrix b) { 39 Matrix rt(r, b.c); 40 assert(c == b.r); 41 for (int i = 0; i < r; i++) 42 for (int j = 0; j < b.c; j++) { 43 rt[i][j] = 0; 44 for (int k = 0; k < c; k++) 45 rt[i][j] = (rt[i][j] + a[i][k] * 1ll * b[k][j]) % M; 46 } 47 return rt; 48 } 49 50 int* operator [] (int p) { 51 return a[p]; 52 } 53 }Matrix; 54 55 const int N = 3e4 + 6; 56 57 Matrix qpow(Matrix a, ll p) { 58 assert(p >= 0); 59 Matrix pa = a, rt(3, 3); 60 for (int i = 0; i < 3; i++) 61 for (int j = 0; j < 3; j++) 62 rt[i][j] = (i == j); 63 for ( ; p; p >>= 1, pa = pa * pa) 64 if (p & 1) 65 rt = rt * pa; 66 return rt; 67 } 68 69 int n; 70 ll m; 71 pli ad[N], rm[N]; 72 Segment ss[N]; 73 74 inline void init() { 75 scanf("%d"Auto, &n, &m); 76 int a; 77 ll l, r; 78 for (int i = 1; i <= n; i++) { 79 scanf("%d"Auto""Auto, &a, &l, &r); 80 ad[i].first = l, ad[i].second = a - 1; 81 rm[i].first = r + 1, rm[i].second = a - 1; 82 } 83 } 84 85 Matrix mktrans(int os, int ns) { 86 Matrix rt(3, 3); 87 for (int i = 0; i < 3; i++) 88 for (int j = 0; j < 3; j++) 89 rt[i][j] = (abs(i - j) <= 1 && !(os & (1 << i)) && !(ns & (1 << j))); 90 return rt; 91 } 92 93 int ts[4], tp = 0; 94 inline void solve() { 95 sort(ad + 1, ad + n + 1); 96 sort(rm + 1, rm + n + 1); 97 int ca = 1, cb = 1; 98 ll st = 1, ed; 99 while (ca <= n || cb <= n) { 100 while (ca <= n && ad[ca].first == st) 101 ts[ad[ca].second]++, ca++; 102 while (cb <= n && rm[cb].first == st) 103 ts[rm[cb].second]--, cb++; 104 105 int sta = 0; 106 for (int i = 0; i < 3; i++) 107 if (ts[i]) 108 sta |= (1 << i); 109 110 ed = m + 1; 111 if (ca <= n) 112 ed = ad[ca].first; 113 if (cb <= n) 114 ed = min(ed, rm[cb].first); 115 ss[++tp] = Segment(st, ed - 1, sta); 116 st = ed; 117 } 118 119 Matrix f(1, 3); 120 f[0][0] = f[0][2] = 0, f[0][1] = 1; 121 Matrix T = mktrans(ss[1].sta, ss[1].sta); 122 f = f * qpow(T, ss[1].r - ss[1].l); 123 // cerr << f[0][0] << " " << f[0][1] << " " << f[0][2] << endl; 124 for (int i = 2; i <= tp; i++) { 125 T = mktrans(ss[i - 1].sta, ss[i].sta); 126 f = f * T; 127 T = mktrans(ss[i].sta, ss[i].sta); 128 f = f * qpow(T, ss[i].r - ss[i].l); 129 // cerr << ss[i].l << " " << ss[i].r << endl; 130 // cerr << f[0][0] << " " << f[0][1] << " " << f[0][2] << endl; 131 } 132 printf("%d\n", f[0][1]); 133 } 134 135 int main() { 136 init(); 137 solve(); 138 return 0; 139 }
Problem G Castle Defense
题目大意
有片城墙被分为$n$个防守段。第$i$段有$a_{i}$个弓箭手。定义一段防守段的防御力是距离它的距离不超过$r$的所有防守段的弓箭手数量之和。定义这片城墙的稳定程度是每个防守段的防御力的最小值。
现在有$k$个后备弓箭手,你可将他们分配任何防守段,但不能调动原有的弓箭手。问调动后最大的稳定程度。
显然二分答案。
考虑怎么check。对于一个防御值小于$mid$的防守段$i$,能对它产生贡献的一些防守段中分配的后备弓箭手总数不得小于$mid - def[i[$。
然后设在第$i$个防守段设置的弓箭手数量为$a_{i}$,对它求一个前缀和$s$。
显然使用差分约束,然后连边就很显然了:
- $s_{r} - s_{l - 1} \leqslant mid - def[i], (def[i] < mid)$
- $s[i]\leqslant s[i + 1]$
由于图比较特殊,不必用最短路算法。直接一个for,完事。
(感觉把防守段替换为烽火台食用更佳,总之原题说的是section,实在不知道怎么翻译比较好)。
Code
1 /** 2 * Codeforces 3 * Problem#954G 4 * Accepted 5 * Time: 358ms 6 * Memory: 13700k 7 */ 8 #include <bits/stdc++.h> 9 #ifndef WIN32 10 #define Auto "%lld" 11 #else 12 #define Auto "%I64d" 13 #endif 14 using namespace std; 15 typedef bool boolean; 16 17 #define ll long long 18 19 const int N = 5e5 + 5; 20 21 int n, r; 22 ll k; 23 int ar[N]; 24 ll ps[N], def[N]; 25 ll f[N]; 26 27 inline void init() { 28 scanf("%d%d"Auto, &n, &r, &k); 29 for (int i = 1; i <= n; i++) 30 scanf("%d", ar + i); 31 for (int i = 1; i <= n; i++) 32 ps[i] = ps[i - 1] + ar[i]; 33 } 34 35 boolean check(ll mid) { 36 memset(f, 0, sizeof(f)); 37 for (int i = 1; i <= n; i++) { 38 f[i] = max(f[i - 1], f[i]); 39 if (def[i] >= mid) 40 continue; 41 int l = max(i - ::r, 1), r = min(i + ::r, n); 42 f[r] = max(f[l - 1] + mid - def[i], f[r]); 43 } 44 return f[n] <= k; 45 } 46 47 inline void solve() { 48 for (int i = 1; i <= n; i++) { 49 int l = max(i - ::r, 1), r = min(i + ::r, n); 50 def[i] = ps[r] - ps[l - 1]; 51 } 52 53 ll l = 0, r = 2e18; 54 while (l <= r) { 55 ll mid = (l + r) >> 1; 56 if (check(mid)) 57 l = mid + 1; 58 else 59 r = mid - 1; 60 } 61 printf(Auto, l - 1); 62 } 63 64 int main() { 65 init(); 66 solve(); 67 return 0; 68 }
Problem H Path Counting
题目大意
给定一棵深度为$n$的树,根节点的深度为1,深度为$i$的点的度数为$a_{i}$,保证$a_{n} = 0$。要求对于每个$1\leqslant d \leqslant 2n - 2$,输出长度为$d$的简单无向路径数量对模$10^9 + 7$的剩余。
现在说一下一个非常常见的计数对象:取路径的最高点。
发现每个深度的每个子树的情况完全相同,因此只需要对每个深度$i$计算答案,然后乘上一个常数$c_{i}$。
设$g_{i,d}$表示在深度为$i$的一个子树中,距离根节点$d$条边的点的数量。
那么有:
$ans_{d} = \sum_{i = 1}^{n}\left[g_{i, d} + \left(\sum_{j = 1}^{\left \lfloor d/2 \right \rfloor}g_{i + 1, j - 1}\cdot g_{i +1, d - j - 1}\right )a_{i}(a_{i} - 1) - [2\mid d]g_{i + 1,d/2 - 1}^2 \right ]c_{i}$
然后来慢慢解释这个式子。
$g_{i, d}$是从当前点出发的路径数量,$中间求和是拼接两条路径,为了防止算重,选择两个不同的子树,并要求第一个子树选择的路径长度小于等于第二个子树中选择,当选择路径长度不同时,正反都可以,所以答案乘2,再减去选择路径长度相同的一部分的答案就行了。
然后开心地发现这个式子是三方的。
接着发现$g, c$都是可以直接算的:
$g_{i, d}=\prod_{j = 0}^{d - 1}a_{i + j}$
$c_{i} = \prod_{j = 1}^{i - 1}a_{j}$
中间的一坨求和式子可以看成,枚举任意$i$,然后在$i + 1$后面选择两段连续的$a$,两段都要包含$a_{i + 1}$,长度和为$d - 2$,再把这两段的积乘起来。
然后很容易发现,设$f_{i, d} = \sum_{j = 0}^{\left \lfloor d/2 \right \rfloor}g_{i, j}\cdot g_{i, d - j}$,那么算$f_{i + 1, d - 2}$的时候可以把$j = 0$的一部分先刨开,然后除以$a_{i}^{2}$就行了。
$f_{i + 1, d - 2} = (f_{i, d} - g_{i, d})a_{i}^{-2}$
于是这个zz做法可以实现$O(n^{2})$。常数贼大,跑得贼慢,sad。。。
然后看了看标算。标算取路径两端点作为计数点,这样每条路径会被计算2次,答案除以2就行了。对于以某个点为端点的路径可以分成两种,第一种是向上走(子树外),第二种是向下走(子树内)。第二部分显然,第一部分转移考虑它走到父节点后的方向,时间复杂度一样,但常数小很多。
1 /** 2 * Codeforces 3 * Problem#954H 4 * Accepted 5 * Time: 1544ms 6 * Memory: 200k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int N = 5005, M = 1e9 + 7; 13 14 void exgcd(int a, int b, int& x, int& y) { 15 if (!b) 16 x = 1, y = 0; 17 else { 18 exgcd(b, a % b, y, x); 19 y -= (a / b) * x; 20 } 21 } 22 23 int inv(int a, int n) { 24 int x, y; 25 exgcd(a, n, x, y); 26 return (x < 0) ? (x + n) : (x); 27 } 28 29 int n; 30 int inv2 = (M + 1) >> 1; 31 int ar[N], br[N]; 32 int pro[N]; 33 int ans[N << 1]; 34 int f[2][N << 1]; 35 36 int add(int a, int b) { 37 a += b; 38 if (a >= M) 39 a -= M; 40 return a; 41 } 42 43 int sub(int a, int b) { 44 a -= b; 45 if (a < 0) 46 a += M; 47 return a; 48 } 49 50 inline void init() { 51 scanf("%d", &n); 52 for (int i = 1; i < n; i++) 53 scanf("%d", ar + i); 54 ar[n] = 0; 55 for (int i = 1; i <= n; i++) 56 br[i] = inv(ar[i], M); 57 } 58 59 int pow2(int x) { 60 return x * 1ll * x % M; 61 } 62 63 inline void solve() { 64 int cur = 0, nxt = 1, P = 1; 65 pro[0] = 1; 66 for (int i = 1; i <= n; i++) 67 pro[i] = pro[i - 1] * 1ll * ar[i] % M; 68 f[cur][0] = 1; 69 for (int d = 1; d <= ((n - 1) << 1); d++) 70 for (int i = max(0, d - n); i <= n && i <= d && i <= d - i; i++) 71 f[cur][d] = add(f[cur][d], pro[i] * 1ll * pro[d - i] % M); 72 for (int i = 1; i < n; i++) { 73 if (i > 1) { 74 pro[0] = 1; 75 for (int j = 0; i + j <= n; j++) 76 pro[j + 1] = pro[j] * 1ll * ar[i + j] % M; 77 } 78 for (int d = 2, x, C; d <= ((n - i) << 1); d++) { 79 x = ((d <= n - i) ? (pro[d]) : (0)); 80 C = ar[i] * 1ll * (ar[i] - 1) % M * P % M; 81 f[nxt][d - 2] = sub(f[cur][d], x) * 1ll * br[i] % M * 1ll * br[i] % M; 82 ans[d] = add(ans[d], f[nxt][d - 2] * 1ll * C % M); 83 if (!(d & 1)) 84 ans[d] = sub(ans[d], pow2(pro[d >> 1] * 1ll * br[i] % M) * 1ll * C % M * inv2 % M); 85 } 86 for (int d = 1; d <= n - i; d++) 87 ans[d] = add(ans[d], pro[d] * 1ll * P % M); 88 P = P * 1ll * ar[i] % M; 89 swap(cur, nxt); 90 } 91 92 for (int i = 1; i <= (n - 1) << 1; i++) 93 printf("%d ", ans[i]); 94 } 95 96 int main() { 97 init(); 98 solve(); 99 return 0; 100 }
Problem I Yet Another String Matching Problem
题目大意
定义一个操作是将串内的一种字符改成另一种字符。定义两个串的编辑距是使得这两个串相等的最少操作数。
给定两个串$S, T\ \ \left(|S|\leqslant |T|\right)$,询问$S$的每个串长等于$|T|$的子串和$|T|$的编辑距。字符集 'a' ~ 'f'
进行一种操作相等于让两种字符等价。这样我们可以将1次操作看作在无向图中将两个字符连接起来。
考虑对于$S_{i} \neq T_{j}$,那么$S_{i}$需要和$T_{j}$等价,那么在无向图中连一条边。答案是字符集大小减去连通块个数。
然后讲标算。标算比较套路,注意到边的数量不超过30条,因此想办法判断每条边在每一次询问中是否存在。
考虑枚举这条边$(a, b)$,将$S$出现$a$的地方标为1,将$T$出现$b$的地方也标为1。如果$S_{i} = a \wedge T_{j} = b$,那么以$i - j$作为起点的地方就会存在这样一条边。
然后就是套路了,翻转串$T$,原来的$j$变为了$|T| - j$,贡献的位置就是$|T| + i - j$,然后跑FFT算答案。
剩下的就是暴力了。可以dfs,也可以dsu算连通块的个数。
由于我很懒,不想写FFT,就成了可恶的嘴巴AC选手。
然后来膜一下Execution Time榜rk 1同学的Hash做法。
考虑按顺序枚举$S$的每个子串$S'$,注意到我们只关心连通块的个数。
不幸的是我们暂时不能区分多个连通块,但是我们有办法当前的连通子图不是极大的情况。
由于字符集很小,我们可以尝试暴力枚举一个连通块由哪些字符组成。考虑选择了一个非极大连通子图,那么存在一个点有一条边连向另一个字符。
因此这个选定的字符集在$S'$中出现的所有位置和$T$中出现的所有位置不相同。因此通过判断出现位置的集合是否相同可以判断非极大连通子图。
如何判断选定的字符集是否连成一个连通块?我们按照字符集包含的字符个数的顺序来枚举,然后对于找的一个极大连通子图,我们把些字符ban掉。这样就能保证了。
判断出现位置集合是否相同可以用Hash,每做完一个询问更新一下位置集合的Hash值。
好像还有一个神奇的dfs做法?没看懂。qwq
口胡另一个做法,考虑最多有 $|\Sigma|$ 条边,可以用 Hash 表示出每一种字符出现的所有位置,即 $h_c(s) = \sum_{i = 0}^{|s| - 1} x^i [s_i = c]$。用并查集维护已经有的边形成的连通块。考虑用二分求出下一对不同的字符,每次求每个字符的 Hash 值乘上它的所在连通块的根的标号。 可以做到 $O(n|\Sigma|^2 \log n)$ 或者 $O(nB_{|\Sigma|} + n|\Sigma|\log n)$
Code
1 /** 2 * Codeforces 3 * Problem#954I 4 * Accepted 5 * Time: 62ms 6 * Memory: 1200k 7 */ 8 #include <algorithm> 9 #include <iostream> 10 #include <cstdlib> 11 #include <cstring> 12 #include <cstdio> 13 using namespace std; 14 typedef bool boolean; 15 16 #define ull unsigned long long 17 18 const int N = 125005, S = 1 << 6; 19 const ull base = 5; 20 21 int n, m; 22 char sa[N], sb[N]; 23 ull pow7[N]; 24 ull ps[S], pt[S]; 25 int bit[S], sta[S]; 26 27 inline void init() { 28 scanf("%s%s", sa, sb); 29 n = strlen(sa), m = strlen(sb); 30 pow7[0] = 1; 31 for (int i = 1; i < n; i++) 32 pow7[i] = pow7[i - 1] * base; 33 } 34 35 void update(ull *ar, int c, ull val, int sgn) { 36 int temp = (1 << c); 37 for (int s = temp; s < S; s = (s + 1) | temp) 38 ar[s] += val * sgn; 39 } 40 41 boolean cmp(const int& a, const int& b) { 42 return bit[a] < bit[b]; 43 } 44 45 inline void prepare() { 46 bit[0] = 0; 47 for (int i = 1; i < S; i++) 48 bit[i] = bit[i - (i & (-i))] + 1; 49 for (int i = 0; i < S; i++) 50 sta[i] = i; 51 sort(sta, sta + S, cmp); 52 } 53 54 inline void solve() { 55 for (int i = 0; i < m; i++) 56 update(ps, sa[i] - 'a', pow7[i], 1); 57 for (int i = 0; i < m; i++) 58 update(pt, sb[i] - 'a', pow7[i], 1); 59 60 for (int i = 0; i <= n - m; i++) { 61 int ban = 0, res = 0; 62 for (int j = 0; j < S; j++) { 63 int s = sta[j]; 64 if (!(s & ban)) { 65 if (!ps[s] && !pt[s]) 66 ban |= s; 67 else if (pt[s] * pow7[i] == ps[s]) { 68 res += bit[s] - 1; 69 ban |= s; 70 } 71 } 72 } 73 printf("%d ", res); 74 if (i + m < n) { 75 update(ps, sa[i] - 'a', pow7[i], -1); 76 update(ps, sa[i + m] - 'a', pow7[i + m], 1); 77 } 78 } 79 } 80 81 int main() { 82 init(); 83 prepare(); 84 solve(); 85 return 0; 86 }