题集链接 ;
A Car Show
题意
给定长度为n,由数字1~m组成的数列,问包含全部m个数字的子串个数;
思路
定位每一个左端点,向右寻找最近的符合要求的右界,得到最小子串,右界从此处开始直至序列末尾均合法,依此更新答案;
更新答案后左界左移,再次执行以上过程;
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <bits/stdc++.h> using namespace std;typedef long long ll;int a[100005 ];int main () { int n, m; cin >> n >> m; ll ans = 0 ; for (int i = 1 ; i <= n; i++) { scanf ("%d" , &a[i]); } int l = 1 , r = 1 , ct = 0 ; unordered_map<int , int > mp; for (; r <= n; r++) { mp[a[r]]++; if (mp[a[r]] == 1 ) { ct++; } while (ct == m && l <= r) { ans += n - r + 1 ; mp[a[l]]--; if (!mp[a[l]]) ct--; l++; } } cout << ans; return 0 ; }
B Two Frogs
概率期望
题意
有n个荷叶排成一列,每个荷叶有属性值 a i a_i a i ,当青蛙在i号荷叶上时,下一步可以跳到 [ i + 1 , i + a i ] [i+1,i+a_i] [ i + 1 , i + a i ] 号荷叶上,求两只青蛙从1号荷叶起经过相同次数的跳动,同时到达n号荷叶的概率;
思路
考虑 q [ m ] [ x ] q[m][x] q [ m ] [ x ] 为跳m次到达x号的概率,则有 q [ m ] [ x ] = ∑ i + 1 ⩽ x ⩽ i + a [ i ] q [ m − 1 ] [ i ] a [ i ] q[m][x]=\sum_{i+1\leqslant x\leqslant i+a[i]}\frac {q[m-1][i]}{a[i]} q [ m ] [ x ] = ∑ i + 1 ⩽ x ⩽ i + a [ i ] a [ i ] q [ m − 1 ] [ i ] ;
答案即为 ∑ i = 1 n − 1 q [ i ] [ n ] 2 \sum_{i=1}^{n-1}q[i][n]^2 ∑ i = 1 n − 1 q [ i ] [ n ] 2 ;
考虑计算过程,由于对于每一个 q [ m ] [ x ] q[m][x] q [ m ] [ x ] ,更新的是 q [ m ] [ x + i ] , i ∈ [ 1 , a [ x ] ] q[m][x+i],i\in[1,a[x]] q [ m ] [ x + i ] , i ∈ [ 1 , a [ x ] ] 连续区间的值,我们可以用差分优化这个操作;
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <bits/stdc++.h> using namespace std;typedef long long ll;const ll M = 998244353 ; ll q[8003 ][8003 ];int exs[8003 ]; ll inv[8003 ];int a[8003 ];long long Q_power (long long a, long long b = M - 2 ) { long long res = 1 ; while (b) { if (b & 1 ) res = res * a % M; b >>= 1 ; a = a * a % M; } return res; }int main () { int n; cin >> n; for (int j = 1 ; j <= n; j++) { inv[j] = Q_power (j); } for (int i = 1 ; i <= n; i++) exs[i] = 10000 ; exs[1 ] = 0 ; for (int i = 1 ; i < n; i++) { scanf ("%d" , &a[i]); for (int j = 1 ; j <= a[i]; j++) { exs[i + j] = min (exs[i + j], exs[i] + 1 ); } } q[1 ][0 ]=1 ; for (int i = 1 ; i <= n; i++) { for (int j = exs[i]; j <= i - 1 ; j++) { q[i][j] = q[i - 1 ][j] + q[i][j]; q[i][j] %= M; ll p = q[i][j] * inv[a[i]]; q[i + 1 ][j + 1 ] = (q[i + 1 ][j + 1 ] + p) % M; q[i + a[i] + 1 ][j + 1 ] = (q[i + a[i] + 1 ][j + 1 ] - p) % M; } } ll ans = 0 ; for (int i = exs[n]; i < n; i++) ans += q[n][i] * q[n][i], ans %= M; printf ("%lld" , ans); return 0 ; }
E Longest Increasing Subsequence
构造
题意
构造长度为 n ⩽ 100 n\leqslant100 n ⩽ 1 0 0 的排序,使得最长上升子序列恰好为m个;
思路
将m进行二进制拆分,考虑以下构造方法:
对于每层最右侧的点,其向下的支是累计下层计数,向左的支是累计本层计数;
依照此策略,可以在 3 log ( n ) 3\log(n) 3 log ( n ) 的长度内构造完成;
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <bits/stdc++.h> using namespace std;int main () { int n, t; cin >> t; while (t--) { int ct = 1 ; vector<int > r (90 ) ; cin >> n; for (int i = 0 ; i < 30 ; i++) { if ((n >> i) & 1 ) { r[2 * i + 1 ] = ct++; r[60 + i] = ct++; r[2 * i] = ct++; } else { r[60 + i] = ct++; r[2 * i + 1 ] = ct++; r[2 * i] = ct++; } } printf ("90\n" ); for (int i=0 ;i<90 ;i++) { printf ("%d%c" ,r[i]," \n" [i == 89 ]); } } }
G Magic Spells
manacher,二分
题意
给定k个字符串,找出在每个串都出现的回文串数量;
思路
首先考虑马拉车算法,找出所有字符串进行统计,使用map将字符串从哈希值映射到最近出现时间(1~k);
果不其然地T了,接下来考虑优化:
首先进行剪枝,在固定圆心从大到小遍历半径的过程中,如果发现某串最近出现时间已经是当前,那么该圆心下半径更小的串就不用考虑了;
依然T,继续优化:
考虑二分,对于固定圆心从大到小遍历半径的过程中,存在分界半径i,半径小于i时最近出现时间均是上串,大于i时最近出现时间均早于上串,以此二分快速确定有效半径;
单哈希可以过;
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int maxn = 3e5 + 5 ;char s[maxn * 2 ]; string str;int d[maxn * 2 ], len;void getstr () { str.clear (); int k = 0 ; str += '{' ; k++; for (int i = 0 ; i < len; i++) { str += '}' ; str += s[i]; k += 2 ; } str += '}' ; k++; len = k; }int manacher () { int mx = 0 , id; int maxx = 0 ; for (int i = 0 ; i < len; i++) { if (i < mx) d[i] = min (mx - i + 1 , d[2 * id - i]); else d[i] = 1 ; while (i + d[i] <= str.size () && i - d[i] >= 0 && str[i + d[i]] == str[i - d[i]]) d[i]++; if (d[i] + i - 1 > mx) { mx = d[i] + i - 1 ; id = i; maxx = max (maxx, d[i]); } } return (maxx - 1 ); }const long long MAGIC = 1e12 + 39 ;long long hsh[maxn * 2 ];void init () { hsh[0 ] = 1 ; for (int i = 1 ; i != maxn * 2 ; i++) { hsh[i] = hsh[i - 1 ] * 1013ll % MAGIC; } }long long str_hsh[maxn * 2 ];void _hsh() { str_hsh[0 ] = str[0 ] - '_' ; for (int i = 1 ; i != str.length (); i++) { str_hsh[i] = (str_hsh[i - 1 ] * 1013ll + str[i] - '_' ) % MAGIC; } }long long get_hash (int l, int r) { if (l == 0 ) return str_hsh[r]; auto fna = str_hsh[r] - __int128_t (1 ) * str_hsh[l - 1 ] * hsh[r - l + 1 ] % MAGIC; if (fna < 0 ) fna += MAGIC; return fna % MAGIC; }long long _len_hash(long long raw, int len) { return raw * (long long )3e5 + len; }int main () { int k; long long ans = 0 ; cin >> k; init (); unordered_map<ll, int > mp; for (int kki = 0 ; kki < k; kki++) { cin >> s; len = strlen (s); getstr (); _hsh(); manacher (); for (int i = 0 ; i < len; i++) { for (int j = d[i]; j >= 1 ; j--) { auto hsh = get_hash (i, i + j - 1 ); if (mp[hsh] == kki + 1 ) break ; else if (mp[hsh] != kki) { int l = 1 , r = j; int res = 0 ; while (l <= r) { j = (l + r) / 2 ; hsh = get_hash (i, i + j - 1 ); if (mp[hsh] < kki) { res = j; r = j - 1 ; } else { l = j + 1 ; } } j = res; } else if (mp[hsh] == kki) { mp[hsh] = kki + 1 ; if (mp[hsh] == k && !(str[i + j - 1 ] == '}' || str[i + j - 1 ] == '{' )) ans++; } } } } printf ("%lld\n" , ans); return 0 ; }