2022年中国大学生程序设计竞赛女生专场 ACEGHIL
A. 减肥计划 思路:因为 很大但是 只有 ,那么分两种情况考虑:
对于 的情况,那么一定所有人都比完了,因为 轮一定可以确定最后的获胜者。那么答案就是最重的。
对于 的情况,直接暴力模拟,最多不超过 次。
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 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 2e6 + 10 ;array<int ,2>a[N]; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int n,k; cin>>n>>k; deque<array<int ,2>>q; int maxx = 0 ,pos = 0 ; for (int i = 1 ;i <= n; i++) { int w; cin>>w; a[i] = {w,i}; q.push_back (a[i]); if (w>maxx) maxx = w,pos = i; } if (k>=n) { cout<<pos<<"\n" ; return 0 ; } int cnt = 0 ; while (1 ) { auto fi = q.front (); q.pop_front (); auto se = q.front (); q.pop_front (); bool ok = false ; while (1 ) { if (cnt>=k) { pos = fi[1 ]; ok = true ; break ; } if (fi[0 ]>=se[0 ]) { cnt++; q.push_back (se); se = q.front (); q.pop_front (); } else { q.push_back (fi); q.push_front (se); cnt = 1 ; break ; } } if (ok) break ; } cout<<pos<<"\n" ; return 0 ; }
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 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 2e5 + 10 ;const double pi = 3.1415926535 ;long double a[N];long double pre[N];int main () { int n; long double r,sita; cin>>n>>r>>sita; for (int i = 1 ;i <= n; i++) cin>>a[i]; if (sita>pi) sita = pi*2 -sita; long double ans = sita*r; for (int i = 1 ;i <= n; i++) { ans = min (ans,sita*a[i]+(r-a[i])*2 ); } printf ("%.8Lf\n" ,ans); return 0 ; }
E. 睡觉 思路:这题卡了很久啊,没注意到如果一轮下来清醒度不变的情况。本题分三种情况讨论:
一首歌的时间内能睡着
一轮下来清醒度递减
一轮下来清醒度不变但是仍然满足清醒度 的条件
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 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 2e5 + 10 ;ll a[N]; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int tc; cin>>tc; while (tc--) { ll x,t,k,n,d; cin>>x>>t>>k>>n>>d; for (int i = 1 ;i <= n; i++) cin>>a[i]; ll tx = x,now = 0 ; bool ok = false ; int cnt = 0 ,i = 1 ; while (cnt<=1e7 ) { if (a[i]<=d)tx--; else tx++; if (tx<=k) now++; else now = 0 ; if (now>=t){ ok = true ; break ; } if (i==n) { if (tx<x) { ok = true ; break ; } } i++; if (i>n)i = 1 ; cnt++; } if (ok||now>n*2 ) cout<<"YES\n" ; else cout<<"NO\n" ; } return 0 ; }
总结:这题细节很多,写的时候要注意考虑对所以情况的分析。
G. 排队打卡 思路:这题直接模拟就可以了,但是注意细节。
【在每一秒结束前会放一次排队的人进入入口,一次最多进入不超过 k个人】的意思是,每次会放k人进入,不超过k人,但是小于k人的时候放当前的所有人。
按照这样的题意的话,那么每一秒的人数是确定的了,我们只要去check每一个有人排队的时刻即可。
为了好写一点,我们加入一个时间为 ,排队人数是 的事件,这样方便check。因为题目输入不会有 人排队的输入( )。
注意以时间为第一关键字排序,还有注意记录 上一个有人排队的时间,因为在这时间之间也是可以放人的。
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 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 5e5 + 10 ;array<ll,2>evt[N]; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); ll t,n,m,k; cin>>t>>n>>m>>k; for (int i = 1 ;i <= m; i++) cin>>evt[i][0 ]>>evt[i][1 ]; m++; evt[m][0 ] = t,evt[m][1 ] = 0 ; sort (evt+1 ,evt+1 +m); ll hav = 0 ; int last = 0 ; ll mn = 1e18 ,pos = 0 ; for (int i = 1 ;i <= m; i++) { hav = max (0ll ,hav - (evt[i][0 ]-last-1 )*k); hav += evt[i][1 ]; last = evt[i][0 ]; if (evt[i][1 ] == 0 && hav!=n) { cout<<"Wrong Record\n" ; return 0 ; } if (evt[i][1 ] != 0 &&evt[i][0 ] >= t) { if (mn>=(hav+1 )/k+((hav+1 )%k!=0 )) mn = (hav+1 )/k+((hav+1 )%k!=0 ),pos = evt[i][0 ]; } hav = max (0ll ,hav-k); } cout<<pos<<" " <<mn<<"\n" ; return 0 ; }
H. 提瓦特之旅 思路:看到【经过路上的第 个点时 需要 的时间完成委托】可以想到 到 号点经过 条边的最小花费。
这样就是典型的 了,再做一个 就可以了。
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 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 1010 ,M = 4e5 +10 ;ll n, m, k, dist[N], backup[N],cnt = 0 ; struct EDGES { ll a, b, w; }edge[M]; ll f[N][N],pre[N]; void bellman_ford () { memset (dist, 0x3f , sizeof dist); memset (f, 0x3f , sizeof f); dist[1 ] = 0 ; for (int i = 1 ; i < n; i++) { memcpy (backup, dist, sizeof dist); for (int j = 0 ; j < m*2 ; j++) { auto e = edge[j]; dist[e.b] = min (dist[e.b], backup[e.a] + e.w); } for (int j = 1 ; j <= n; j++) { f[i][j] = dist[j]; } } } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); cin >> n >> m; for (int i = 0 ; i < m; i++) { ll u,v,w; cin>>u>>v>>w; edge[cnt++] = {u,v,w}; edge[cnt++] = {v,u,w}; } bellman_ford (); int q; cin>>q; while (q--) { int t; cin>>t; for (int i = 1 ;i <= n-1 ; i++) { ll w; cin>>w; pre[i] = pre[i-1 ]+w; } ll res = 1e18 ; for (int i = 1 ;i < n; i++) { if (f[i][t]!=0 ) res = min (res,f[i][t] + pre[i]); } cout<<res<<"\n" ; } cout<<"\n" ; return 0 ; }
引用一篇文章:关于Bellman_ford算法解决有边数限制最短路(打印路径)
I. 宠物对战 思路:一眼字符串哈希+ 。
我先分别预处理出 类和 类的 值,放到 里面。然后做 。
我们定义: 表示,前 位,第 位放 或放 的最少需要的字符串个数。
我们枚举左右端点,转移就是:
1 2 3 4 if (dp[l-1 ][0 ]<(1e10 )&&b.find (ha.get (l,r))!=b.end ()) dp[r][1 ] = min (dp[r][1 ],dp[l-1 ][0 ]+1 ); if (dp[l-1 ][1 ]<(1e10 )&&a.find (ha.get (l,r))!=a.end ()) dp[r][0 ] = min (dp[r][0 ],dp[l-1 ][1 ]+1 );
dp思路很容易,但是注意用双哈希防止碰撞,还有就是这题卡常,注意一下。
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 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 5e5 + 10 ;typedef pair<long long , long long > pll;struct DoubleStringHash { vector<long long > h1, h2, w1, w2; long long base1 = 131 , base2 = 13331 ; long long p1 = 1e9 + 7 , p2 = 1e9 + 9 ; void init (string s) { int len = s.size (); s = " " + s; h1.resize (len + 1 ), w1.resize (len + 1 ); h2.resize (len + 1 ), w2.resize (len + 1 ); h1[0 ] = 0 , w1[0 ] = 1 ; h2[0 ] = 0 , w2[0 ] = 1 ; for (int i = 1 ; i <= len; i++) { h1[i] = (h1[i - 1 ] * base1 + s[i]) % p1, w1[i] = w1[i - 1 ] * base1 % p1; h2[i] = (h2[i - 1 ] * base2 + s[i]) % p2, w2[i] = w2[i - 1 ] * base2 % p2; } } pll get (int l, int r) { return {(h1[r] - h1[l - 1 ] * w1[r - l + 1 ] % p1 + p1) % p1, (h2[r] - h2[l - 1 ] * w2[r - l + 1 ] % p2 + p2) % p2}; } }ha; ll dp[N][3 ],lena[N],lenb[N]; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int n; cin>>n; set<pll>a,b; for (int i = 1 ;i <= n; i++) { string x; cin>>x; ha.init (x); int sz = x.size (); a.insert (ha.get (1 ,sz)); } int m; cin>>m; for (int i = 1 ;i <= m; i++) { string x; cin>>x; ha.init (x); int sz = x.size (); b.insert (ha.get (1 ,sz)); } string s; cin>>s; ha.init (s); int sz = s.size (); memset (dp,0x3f ,sizeof (dp)); dp[0 ][1 ] = dp[0 ][0 ] = 0 ; for (int l = 1 ; l <= sz; l++) { for (int r = l; r <= sz; r++) { if (dp[l-1 ][0 ]<(1e10 )&&b.find (ha.get (l,r))!=b.end ()) dp[r][1 ] = min (dp[r][1 ],dp[l-1 ][0 ]+1 ); if (dp[l-1 ][1 ]<(1e10 )&&a.find (ha.get (l,r))!=a.end ()) dp[r][0 ] = min (dp[r][0 ],dp[l-1 ][1 ]+1 ); } } ll ans = min (dp[sz][0 ],dp[sz][1 ]); if (ans>=1e10 ) cout<<-1 <<"\n" ; else cout<<ans<<"\n" ; return 0 ; }
L. 彩色的树 思路:DSU on Tree 的模板题。套板子即可。
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 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int mod = 1e9 + 7 ;const int N = 2e5 + 10 ;int n, k, q;vector<int > e[N]; int l[N], r[N], id[N], sz[N], hs[N], tot;int res[N],col[N],cnt[N],dep[N],now;vector<int >del_c[N*2 ]; inline void add (int u) { if (cnt[col[u]]==0 )now++; cnt[col[u]]++; del_c[dep[u]].push_back (col[u]); } inline void del (int depth) { for (auto x : del_c[depth]) { cnt[x]--; if (cnt[x]==0 )now--; } del_c[depth].clear (); } inline void query (int u) { res[u] = now; } void dfs_init (int u,int f) { dep[u] = dep[f] + 1 ; l[u] = ++tot; id[tot] = u; sz[u] = 1 ; hs[u] = -1 ; for (auto v : e[u]) { if (v == f) continue ; dfs_init (v, u); sz[u] += sz[v]; if (hs[u] == -1 || sz[v] > sz[hs[u]]) hs[u] = v; } r[u] = tot; } void dfs_solve (int u, int f, bool keep) { for (auto v : e[u]) { if (v != f && v != hs[u]) { dfs_solve (v, u, false ); } } if (hs[u] != -1 ) { dfs_solve (hs[u], u, true ); } for (auto v : e[u]) { if (v != f && v != hs[u]) { for (int x = l[v]; x <= r[v]; x++) { if (dep[u]+k>=dep[id[x]]) add (id[x]); } } } add (u); del (dep[u]+k+1 ); query (u); if (!keep) { for (int x = l[u]; x <= r[u]; x++) del (dep[id[x]]); } } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); cin>>n>>k; for (int i = 1 ;i <= n; i++) cin>>col[i]; for (int i = 1 ; i < n; i++) { int u, v; cin>>u>>v; e[u].push_back (v); e[v].push_back (u); } dfs_init (1 , 0 ); dfs_solve (1 , 0 , false ); cin>>q; for (int i = 1 ;i <= q; i++) { int u; cin>>u; cout<<res[u]<<"\n" ; } return 0 ; }
总结 总的来说,没有难题,但关键是细心+读题仔细。会写但不一定写的对,我经常这样,《手比脑子快》。这点要改,以后写之前先理清思路,还要考虑清楚怎么写会好写一点,这点很重要!!!有些写法或许可以,但是复杂,写错的可能性大。这种时候要考虑更好写的写法,比如G题,其实就很简单,但是一开始的写法比较丑。