A. 题目描述: 数据范围:
题解: 代码: B. 题目描述: 数据范围:
题解: 代码: C. 题目描述: 数据范围:
题解: 代码: 题目描述: 给出一个长度为 $ n $ 的序列,给出一个模数 $ p $ 。序列中连续的两个可以合并,可以多次合并,计算最多有多少个 $ \mod {p} =0 $
数据范围: $ 1\le T \le 20 $
$ 1\le n,p\le10^5,\sum n \le 10^6 $
$ 1\le a_i \le 10^5 $
题解: 本质上是让给序列分段,最多有多少段能够被 $ p $ 整除。由于需要使用到和,所以可以使用前缀和 $ sum $ 维护。 对前缀和中每一个数取一下模,然后满足的就是前缀和序列中两个值相等的情况,两值相等说明中间的和刚好可以被 $ p $ 整除。 $ dp $ 可以解决,使用一个数组保存 $ pre[sum[i]] $ 。 $ dp[i] = max(dp[i-1], dp[pre[sum[i]]] + 1) $
也可以贪心的搞,由于每一段都是非连续的,所以可以把当前出现的全部存起来,然后遇到重复的搞一下,删除全部的。使用 $ map $
代码: 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 ll long long using namespace std ;const int maxn = 1e5 + 10 ;int t;int n, p;int a[maxn];int dp[maxn];int last[maxn];int main () {#ifndef ONLINE_JUDGE #ifdef COMP freopen("/mnt/f/文档/课件/ACM比赛/2020hdu多校/第三场/data/1004.in" , "r" , stdin ); freopen("out.txt" , "w" , stdout ); #else freopen("in.txt" , "r" , stdin ); #endif #endif ios::sync_with_stdio(false ); cin .tie(0 ); cout .tie(0 ); cin >> t; while (t--) { cin >> n >> p; for (int i = 1 ; i <= n; i++) { cin >> a[i]; a[i] = (a[i - 1 ] + a[i]) % p; } memset (last, -1 , sizeof (last)); last[0 ] = 0 ; dp[0 ] = 0 ; for (int i = 1 ; i <= n; i++) { dp[i] = dp[i-1 ]; if (last[a[i]] != -1 ) { dp[i] = max(dp[i], dp[last[a[i]]] + 1 ); } last[a[i]] = i; } cout << dp[n] << endl ; } return 0 ; }
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 #include <bits/stdc++.h> #define ll long long using namespace std ;const int maxn = 1e5 + 10 ;int t;int n, p;int a[maxn];int dp[maxn];int last[maxn];map <int , int > mp;int main () {#ifndef ONLINE_JUDGE #ifdef COMP freopen("/mnt/f/文档/课件/ACM比赛/2020hdu多校/第三场/data/1004.in" , "r" , stdin ); freopen("out.txt" , "w" , stdout ); #else freopen("in.txt" , "r" , stdin ); #endif #endif ios::sync_with_stdio(false ); cin .tie(0 ); cout .tie(0 ); cin >> t; while (t--) { cin >> n >> p; for (int i = 1 ; i <= n; i++) { cin >> a[i]; a[i] = (a[i - 1 ] + a[i]) % p; } mp.clear(); mp[0 ] = 1 ; int ans = 0 ; for (int i = 1 ; i <= n; i++) { if (mp.count(a[i])) { ans++; mp.clear(); } mp[a[i]] = 1 ; } cout << ans << endl ; } return 0 ; }
题目描述: $ 1 $ 号表示读题手, $ 2 $ 号表示代码手。从 $ n $ 个人中选出队员组成队伍。增加人之间的熟悉关系,具有传递性。限制如下:
一个队伍至少有两名代码手 队伍内部不能有相互熟悉的人。 求有多少种组合 $ (\mod (1e9 + 7)) $
数据范围: $ 1\le T \le 10 $
$ 1\le n \le 10^5 $
题解: 之前我也想到了使用并查集维护,每个并查集集合记录 $ 1 $ 的人数, $ 2 $ 的人数。先算出来,然后增加关系的时候再减去一部分。
假设目前有两个集合。 $ \{a_1, a_2\} $ , $ \{b_1, b_2\} $
合并的时候需要减去一部分
左 $ 1 $ 右 $ 2 $ 余 $ 2 $
左 $ 2 $ 右 $ 1 $ 余 $ 2 $
左 $ 2 $ 右 $ 2 $ 余 $ 2 $
左 $ 2 $ 右 $ 2 $ 余 $ 1 $
总共需要减去这四种
算初始的 $ sum $ 出现了一些问题。在除之前不能使用 $ mod $ 。因为除法不满足类似 $ (a\times b) mod(c) = a \mod(c)\times b \mod (c) $ 的性质
代码: 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 <bits/stdc++.h> #define ll long long using namespace std ;const int maxn = 1e5 + 10 ;const ll mod = 1e9 + 7 ;int fa[maxn], a[maxn][2 ];int findFa (int u) { return fa[u] == u ? u : (fa[u] = findFa(fa[u])); }int t, n, m;void init () { for (int i = 0 ; i <= n; i++) { a[i][0 ] = a[i][1 ] = 0 ; fa[i] = i; } } int main () {#ifndef ONLINE_JUDGE freopen("/mnt/f/文档/课件/ACM比赛/2020hdu多校/第三场/data/1005.in" , "r" , stdin ); freopen("out.txt" , "w" , stdout ); #endif scanf ("%d" , &t); int x; while (t--) { scanf ("%d" , &n); init(); int one = 0 , two = 0 ; for (int i = 1 ; i <= n; i++) { scanf ("%d" , &x); if (x == 1 ) one++; else two++; x--; a[i][x]++; } ll sum = (two * (two - 1L L) * (two - 2L L) / 6 % mod + two * (two - 1L L) * one / 2 % mod) % mod; printf ("%lld\n" , sum); int u, v; for (int i = 1 ; i < n; i++) { scanf ("%d%d" , &u, &v); u = findFa(u); v = findFa(v); sum = ((sum - ((ll)a[u][0 ] * a[v][1 ] % mod * (two - a[u][1 ] - a[v][1 ]) % mod)) % mod + mod) % mod; sum = ((sum - ((ll)a[u][1 ] * a[v][0 ] % mod * (two - a[u][1 ] - a[v][1 ]) % mod)) % mod + mod) % mod; sum = ((sum - ((ll)a[u][1 ] * a[v][1 ] % mod * (two - a[u][1 ] - a[v][1 ]) % mod)) % mod + mod) % mod; sum = ((sum - ((ll)a[u][1 ] * a[v][1 ] % mod * (one - a[u][0 ] - a[v][0 ]) % mod)) % mod + mod) % mod; printf ("%lld\n" , sum); a[v][0 ] += a[u][0 ]; a[v][1 ] += a[u][1 ]; fa[u] = v; } } return 0 ; }
F. 题目描述: 数据范围:
题解: 代码: 题目描述: 一个无向完全图,任意两点之间都有一条边,带边权。
删除 $ k $ 条边之后的最短路最大值。
数据范围: $ 1\le T \le 100 $
$ 3\le n \le 50, 1\le k \le min(n-2, 5) $
$ 1\le w \le 10^4 $
题解: 没想到题解还真是枚举删除,变成了搜索问题了。
注意记录路径开的 $ pre $ 数组如果开为全局需要使用二维的,防止不同的 $ dfs $ 深度对其破坏。也可以直接使用局部的,拷贝一下。
$ wsl $ 跑的也太慢了,跑了 $ 30s $ 左右,交上去居然过了,才跑了 $ 5s $ 多。
代码: 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 #include <bits/stdc++.h> #define ll long long using namespace std ;typedef pair<int , int > pii;const int maxn = 50 + 10 ;const ll mod = 1e9 + 7 ;const int inf = 0x3f3f3f3f ;const double eqs = 1e-5 ;int t, n, k, m;int g[maxn][maxn];int ans;int pre[maxn][maxn];int dis[maxn];int now;void dijstra (int s, int cur) { priority_queue<pii, vector <pii>, greater<pii>> q; memset (dis, 0x3f , sizeof (dis)); dis[s] = 0 ; q.push(make_pair(0 , s)); while (!q.empty()) { pii out = q.top(); q.pop(); int u = out.second; if (dis[u] < out.first) continue ; for (int i = 1 ; i <= n; i++) { if (g[u][i] == inf || g[u][i] == 0 ) continue ; if (dis[i] > dis[u] + g[u][i]) { dis[i] = dis[u] + g[u][i]; pre[cur][i] = u; q.push(make_pair(dis[i], i)); } } } } void dfs (int cur) { dijstra(1 , cur); if (cur == k + 1 ) { ans = max(ans, dis[n]); return ; } int v = n, u; while (v != 1 ) { u = pre[cur][v]; int tmp = g[u][v]; g[u][v] = g[v][u] = inf; dfs(cur + 1 ); g[u][v] = g[v][u] = tmp; v = u; } } int main () {#ifndef ONLINE_JUDGE freopen("/mnt/f/文档/课件/ACM比赛/2020hdu多校/第三场/data/1007.in" , "r" , stdin ); freopen("out.txt" , "w" , stdout ); #endif scanf ("%d" , &t); int u, v, w; while (t--) { scanf ("%d %d" , &n, &k); for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= n; j++) g[i][j] = i == j ? 0 : inf; for (int i = 1 ; i <= n * (n - 1 ) / 2 ; i++) { scanf ("%d%d%d" , &u, &v, &w); g[u][v] = g[v][u] = w; } ans = 0 ; dfs(1 ); printf ("%d\n" , ans); } return 0 ; }
题目描述:
一个等边三角形,给出其中一个点的初始速度 $ v_x, v_y $ ,碰到边界发生碰撞,遵循反射定律。求第 $ k $ 次碰撞发生的时间。保证了不会撞到角中。
数据范围: $ 1\le T \le 10^4 $
$ 1\le L \le 10^4, -10^4 \le x, y, v_x, v_y \le 10^4 $
$ 1\le k \le 10^6 $
题解: 很明显需要考虑将三角形翻折镜像等操作,让小球一直沿着直线跑。
二分答案。
首先考虑与 $ x $ 轴平行的先,只需要看 $ vy $ 就可以算,然后将速度和坐标旋转一下,继续同样的方法处理。
代码: 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 #include <bits/stdc++.h> using namespace std ;const int MAXN=10005 ;const double EPS=1e-6 ;const double PI=4.0 *atan (1.0 );const double base=2 *PI/3 ;int k,T;double L,vx,vy,x,y,h;pair<double,double> get_point(const pair<double,double> &a,const double &rad) { return make_pair(a.first*cos (rad)-(a.second-h/3 )*sin (rad),a.first*sin (rad)+(a.second-h/3 )*cos (rad)+h/3 ); } bool ck (double times) { long long has=0 ; has+=abs (floor (get_point(make_pair(x+vx*times,y+vy*times),base*0 ).second/h)); has+=abs (floor (get_point(make_pair(x+vx*times,y+vy*times),base*1 ).second/h)); has+=abs (floor (get_point(make_pair(x+vx*times,y+vy*times),base*2 ).second/h)); return has>=k; } int main () { int T; scanf ("%d" ,&T); while (T--) { scanf ("%lf %lf %lf %lf %lf %d" ,&L,&x,&y,&vx,&vy,&k); h=L*sqrt (3 )/2 ; double l=0 ,r=1e11 ,mid; while (r-l>EPS) { mid=(l+r)/2 ; if (ck(mid)) r=mid; else l=mid; } printf ("%.8lf\n" ,(l+r)/2 ); } return 0 ; }
题目描述: 给出一个序列,全是 $ () $ ,然后将 $ $ 替换,或者把 $ * $ 删去,能否得到一个匹配好的序列。按照字典序最小,选出字典序最小的。
数据范围: $ 1\le T \le 10^5 $
$ 1\le n \le 10^5,\sum n \le 5 \times10^6 $
题解:
这是题解中的证明。左侧是补 $ ( $ ,右侧是补 $ ) $
所以可以使用队列把 $ * $ 存起来,遇到不够的时候就拿出来补
代码: 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 #include <bits/stdc++.h> #define ll long long using namespace std ;const int maxn = 1e5 + 10 ;int t, n;queue <int > q;char s[maxn];int main () {#ifndef ONLINE_JUDGE freopen("in.txt" , "r" , stdin ); #endif ios::sync_with_stdio(false ); cin .tie(0 ); cout .tie(0 ); cin >> t; while (t--) { cin >> s; int sum = 0 ; n = strlen (s); while (q.size()) q.pop(); for (int i = 0 ; i < n; i++) { if (s[i] == '(' ) sum++; else if (s[i] == '*' ) q.push(i); else sum--; if (sum < 0 ) { if (q.empty()) break ; s[q.front()] = '(' , q.pop(); sum++; } } if (sum < 0 ) { cout << "No solution!" << endl ; continue ; } if (sum == 0 ) { for (int i = 0 ; i < n; i++) { if (s[i] != '*' ) { cout << s[i]; } } cout << endl ; continue ; } sum = 0 ; while (q.size()) q.pop(); for (int i = n - 1 ; i >= 0 ; i--) { if (s[i] == '*' ) q.push(i); else if (s[i] == '(' ) sum--; else sum++; if (sum < 0 ) { if (q.empty()) break ; s[q.front()] = ')' ; q.pop(); sum++; } } if (sum < 0 ) { cout << "No solution!" << endl ; continue ; } for (int i = 0 ; i < n; i++) { if (s[i] != '*' ) { cout << s[i]; } } cout << endl ; } return 0 ; }
J. 题目描述: 数据范围:
题解: 代码: