算是最近做的几道题里觉得值得回顾一下的吧
ABC295_D
题意:给定一个由0~9组成的字符串S, 且∣ S ∣ < = 5 e 5 |S| <= 5e5 ∣ S ∣ < = 5 e 5 ,求存在多少个l , r l,r l , r 满足S l , . . . S r S_l,...S_r S l , . . . S r 中满足每个数字出现偶数次个
思路:假设我们只考虑 l l l 到 r r r 中的一种数字,如果我们求该数字的个数前缀和,如果满足题意的话那么 $sum[r] - sum[l - 1] = 0 (mod 0) $,那么显然这俩个的奇偶性是相同的。
那么我们可以用map来从前往后记录每种出现的状态的个数,用pre[i][j]来记录数字i到j位置出现的个数。
长度为10的状态st表示每一个数字出现个数的奇偶性
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 #include <bits/stdc++.h> #define rep(i, n, m) for(int i = n; i <= m; ++i) #define per(i, n, m) for(int i = n; i >= m; --i) #define ll long long using namespace std;const int N = 5e5 + 10 ;string st; int pre[10 ][N];ll ans; map<string, int > p; void cal (int u) { rep (i, 0 , 9 ) st[i] = pre[i][u] % 2 + '0' ; if (p.count (st))ans += 1ll * p[st]; p[st] ++; } int main () { string s; cin >> s; int n = s.size (); s = ' ' + s; st.resize (10 ); rep (i, 0 , 9 ) st[i] = '0' ; p[st] ++; rep (i, 1 , n){ rep (j, 0 , 9 ){ pre[j][i] = pre[j][i - 1 ] + (j == s[i] - '0' ); } cal (i); } cout << ans; return 0 ; }
ABC295_E
题意:给定 n , m , k n, m, k n , m , k ,
和一个长度为 n n n 的数组,为A 1 , A 2 . . . A n A_1,A_2...A_n A 1 , A 2 . . . A n ,
其中1 < = k < = n < = 2000 , 1 < = m < = 2000 , 0 < = A i < = m 1<=k<=n<=2000, 1<=m<=2000, 0<=A_i<=m 1 < = k < = n < = 2 0 0 0 , 1 < = m < = 2 0 0 0 , 0 < = A i < = m
我们可以选择其中为0的位置,其值随机为 1 到 m 1到m 1 到 m 的一个值,然后求对该数组排序后A k A_k A k 的期望值
思路:据我们所知期望值$E(x)=∑i*p_{x=i} =∑ p_{x>=i} ,那么对于本题,对于每个 ,那么对于本题,对于每个 , 那 么 对 于 本 题 , 对 于 每 个 i∈(1,m), 我们求排序后 k 位置上的 ,我们求排序后k位置上的 , 我 们 求 排 序 后 k 位 置 上 的 A_k >= i$的概率对答案的贡献
其中,我们统计原数组中比i大的数字个数tar和等于0的个数ze,我们若要第k位大于等于i,则还需要n − k + 1 − t a r n-k+1-tar n − k + 1 − t a r 个大于等于i的数,如果我们定义此时的t a r = n − k + 1 − t a r tar=n-k+1-tar t a r = n − k + 1 − t a r ,则当tar小于0时,无论怎么操作都能满足要求,此时对答案的贡献为1.
假设我们需要的t a r − z e > 0 tar-ze>0 t a r − z e > 0 时,说明没有足够的0来满足题目要求,所以此时对答案的贡献都是0
倘若以上情况都不是,我们的贡献为C z e j p j ( 1 − p ) z e − j C_{ze}^jp^j(1-p)^{ze-j} C z e j p j ( 1 − p ) z e − j ,其中p为选中的都是大于i的情况,即p = ( m − i + 1 ) / m p=(m-i+1)/m p = ( m − i + 1 ) / 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <bits/stdc++.h> #define rep(i, n, m) for(int i = n; i <= m; ++i) #define int long long using namespace std;const int N = 2e3 + 10 , MOD = 998244353 ;int a[N], C[N][N];int qmi (int a, int b) { int ans = 1 ; while (b){ if (b & 1 ) ans = ans * a % MOD; a = a * a % MOD; b >>= 1 ; } return ans % MOD; } signed main () { rep (i, 0 , N - 1 ){ rep (j, 0 , i){ if (j == 0 ) C[i][j] = 1 ; else C[i][j] = (C[i - 1 ][j] + C[i - 1 ][j - 1 ]) % MOD; } } int n, m, k; cin >> n >> m >> k; rep (i, 1 , n) cin >> a[i]; int ans = 0 ; rep (i, 1 , m){ int tar = n - k + 1 , ze = 0 ; rep (j, 1 , n){ if (a[j] >= i) tar --; else if (a[j] == 0 ) ze ++ ; } if (tar < 0 || tar - ze > 0 ){ if (tar < 0 ) ans ++; continue ; } int p = (m - i + 1 ) * qmi (m, MOD - 2 ) % MOD; int pp = ((1 - p) % MOD + MOD) % MOD; rep (j, tar, ze){ int x = qmi (p, j), y = qmi (pp, ze - j); int num = C[ze][j] % MOD * x % MOD * y % MOD; ans = (ans + num) % MOD; } } cout << ans; }
How far away
一道LCA题,算板子吧
题意:给定T组数据,每组数据有一棵树,给定n , m n,m n , m ,分别为点数和边数,然后n − 1 n-1 n − 1 行输入两个点及其边长,然后m组询问,每次查询两个结点的最近公共祖先
思路:我们利用倍增的思想,用 f [ i ] [ j ] f[i][j] f [ i ] [ j ] 记录i i i 结点往上( 1 < < j ) (1 << j) ( 1 < < j ) 的祖先,用 $dist[i] 记录 记录 记 录 i到根节点的深度 , 同时用一个 到根节点的深度,同时用一个 到 根 节 点 的 深 度 , 同 时 用 一 个 cost[i][j]$同样用倍增的思路记录路径长度
其中我们用
1 2 3 for(int i = 1; i <= n; ++ i){ lg[i] = lg[i - 1] + (1 << lg[i - 1] == i); }
来地推l g [ i ] lg[i] l g [ 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 #include <iostream> #define rep(i, n, m) for(int i = n; i <= m; ++i) #define per(i, n, m) for(int i = n; i >= m; --i) using namespace std;const int N = 8e5 + 10 ;int w[N], h[N], ne[N], e[N], cnt, lg[N], dist[N], f[N][30 ], cost[N][30 ];void add (int a, int b, int c) { e[cnt] = b, w[cnt] = c, ne[cnt] = h[a], h[a] = cnt++; } void dfs (int u, int fa) { dist[u] = dist[fa] + 1 ; f[u][0 ] = fa; for (int i = 1 ; i <= lg[dist[u]]; ++ i){ f[u][i] = f[f[u][i - 1 ]][i - 1 ]; cost[u][i] = cost[f[u][i - 1 ]][i - 1 ] + cost[u][i - 1 ]; } for (int i = h[u]; ~i; i = ne[i]){ int j = e[i]; if (j != fa){ cost[j][0 ] = w[i]; dfs (j, u); } } } int LCA (int a, int b) { int ans = 0 ; if (dist[a] < dist[b]) swap (a, b); while (dist[a] > dist[b]) ans += cost[a][lg[dist[a] - dist[b]] - 1 ], a = f[a][lg[dist[a] - dist[b]] - 1 ]; if (a == b) return ans; for (int i = lg[dist[a]] - 1 ; i >= 0 ; --i){ if (f[a][i] != f[b][i]){ ans += cost[a][i]; ans += cost[b][i]; a = f[a][i], b = f[b][i]; } } return ans + cost[a][0 ] + cost[b][0 ]; } int main () { int T; cin >> T; while (T--){ memset (h, -1 , sizeof h); int n, m, s; cin >> n >> m; for (int i = 1 ; i <= n; ++ i){ lg[i] = lg[i - 1 ] + (1 << lg[i - 1 ] == i); } rep (i, 1 , n - 1 ){ int a, b, c; cin >> a >> b >> c; add (a, b, c); add (b, a, c); } dfs (1 , 0 ); while (m --){ int a, b; cin >> a >> b; cout << LCA (a, b) << '\n' ; } } return 0 ; }