比赛链接:Dashboard - 3.17 周赛 - Codeforces
B
水题
1 2 3 4 5 6 7 8 9 10 #include <bits/stdc++.h> using namespace std;int main () { int n; cin>>n; if (n<=3 )cout<<1 ; else cout<<n-2 ; return 0 ; }
D
字符串模拟水题
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 #include <bits/stdc++.h> using namespace std;int n,m;string s[30 ]; map<string,pair<int ,int >>mp; set<string>ppp; int cnt=0 ;string str[21 ][100005 ]; bool check[21 ][100005 ];int cor[21 ],all[21 ];int main () { cin>>n; for (int i=0 ;i<n;i++)cin>>s[i],ppp.insert (s[i]); cin>>m; for (int i=0 ;i<m;i++) { string x,y; string flag; cin>>x>>y>>flag; if (ppp.find (x)==ppp.end ())continue ; if (mp[x].first==0 ) { mp[x].first=++cnt; } str[mp[x].first][++mp[x].second]=y; all[mp[x].first]++; if (flag=="correct" )cor[mp[x].first]++; } long long cr=1 ,al=1 ; bool f=1 ; for (int i=0 ;i<n;i++) { if (mp[s[i]].second>1 )f=0 ; cr*=cor[mp[s[i]].first]; al*=all[mp[s[i]].first]; } if (f==0 ) { cout<<cr<<" correct\n" <<al-cr<<" incorrect" ; } else { for (int i=0 ;i<n;i++) { cout<<str[mp[s[i]].first][1 ]<<" " ; if (cor[mp[s[i]].first]==0 )f=0 ; } if (f)cout<<"\ncorrect" ; else cout<<"\nincorrect" ; } return 0 ; }
H
题意:给定 $a,b,c,d$ ,其中 $d$ 可以分配给 $a,b,c$ ,求 $a^2+b^2+c^2+7\times min(a,b,c)$ 的最大值。
假设 $a\geq b\geq c$ ,若不考虑 $min(a,b,c)$ ,那么一定是将 $d$ 全部分配给 $a$ 使结果最大。
若考虑 $min(a,b,c)$ ,那么将 $d$ 同时分配给 $a$ 和 $c$ 可以使结果变大。假设分配 $k$ 给 $a$ ,且 $c$ 不大于 $b$ 。(若 $c=b$ 了,那么需要同时增加 $c$ 和 $b$ 才能改变最后一项,不优)那么此时增加的值为:
$$
delta=[(a+d-k)^2+b^2+(c+k)^2+7\times (c+k)]-[a^2+b^2+c^2+7\times c]
$$
化简后得到:
$$
delta=2k^2+(7-2a-2d+2c)k+2ad+d^2
$$
得到的是一个二次函数,分析可知当对称轴大于0时, $d\geq 4$ 时。因 $c$ 需小于 $b$ ,而当 $d\geq 4$ 时满足:$2\times \frac{2a+2d-2c-7}{4}\geq b-c=k$ ,故此时最大值即在 $k=0$ 时取到。若 $d<4$ ,直接枚举。
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;typedef long long LL;LL d; LL a[4 ]; int main () { int T; cin >> T; while (T --) { cin >> a[1 ] >> a[2 ] >> a[3 ]; cin >> d; sort (a + 1 , a + 1 + 3 , greater <LL>()); if (d >= 4 ) { cout << (a[1 ] + d) * (a[1 ] + d) + a[2 ] * a[2 ] + a[3 ] * a[3 ] + 7 * min (a[2 ] , a[3 ]) << '\n' ; } else { LL res = (a[1 ]) * (a[1 ]) + a[2 ] * a[2 ] + a[3 ] * a[3 ] + 7 * min (a[2 ] , a[3 ]); for (int i = 0 ; i <= d; i ++) for (int j = 0 ; j <= d; j ++) { int k = d - i - j; if (k>=0 ) { LL ans = (a[1 ] + i) * (a[1 ] + i) + (a[2 ] + j) * (a[2 ]+j) + (a[3 ]+k) * (a[3 ]+k) + 7 * min (a[1 ] + i, min (a[2 ] +j , a[3 ]+k)); if (ans > res)res = ans; } } cout << res << '\n' ; } } return 0 ; }
I
题意:一台电脑容量为 $c$ ,现在有 $n$ 个程序,每个程序下载需要容量 $d$ ,安装需要容量 $s$ ,每个程序在下载后安装时,占的容量会马上从 $d$ 转换成 $s$ 。问最多能下载多少个程序,并打印出下载顺序。
首先考虑对于两个程序 $d_1,s_1$ 与 $d_2,s_2$ ,若先安装第二个,那么需要满足:
$$
s_2+d_1\leq s_1+d_2
$$
即:
$$
d_2-s_2\geq d_1-s_1
$$
因此全部按照 $d-s$ 进行排序,此时选择顺序一定是按照这个顺序,因此可以线性背包解决。
考虑设状态为 $dp[i][j]$ ,第二位表示安装好程序后占用的空间。状态转移方程为:
$$
dp[i][j]=max(dp[i-1][j],dp[i-1][j-s[i]]+1)
$$
但是需注意限制条件:$c-(j-s[i])\geq d[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 #include <bits/stdc++.h> using namespace std;const int MAXN=1e4 +5 ;struct Node { int d,s,id; }a[MAXN]; int f[505 ][MAXN],n,V;bool cmp (const Node a, const Node b) { return (a.d - a.s) > (b.d - b.s); } int main () { ios::sync_with_stdio (false ); cin>>n>>V; for (int i=1 ;i<=n;i++) { cin >> a[i].d >> a[i].s; a[i].id = i; } sort (a+1 ,a+n+1 ,cmp); memset (f,0 ,sizeof (f)); for (int i=1 ;i<=n;i++) { for (int j=V;j>=0 ;j--) { f[i][j] = f[i-1 ][j]; if (V-(j-a[i].s) < a[i].d) continue ; if (j < a[i].s) continue ; f[i][j] = max (f[i-1 ][j], f[i-1 ][j-a[i].s]+1 ); } } int Max = 0 , v = 0 ; for (int i=0 ;i<=V;i++) { if (f[n][i] > Max) { Max = f[n][i]; v = i; } } cout << f[n][v] << "\n" ; if (f[n][v] == 0 )return 0 ; int q[MAXN],cnt = 0 ; for (int i=n;i>=1 &&v>0 ;i--) { if (f[i][v] == f[i-1 ][v-a[i].s]+1 && v-a[i].s>=0 ) { q[++cnt] = a[i].id; v -= a[i].s; } } for (int i=cnt;i>=1 ;i--) { cout << q[i] << " " ; } cout<<endl; return 0 ; }
K
题意:有 $n$ 个人参加锦标赛($n\leq 4096$),两两对决,赢家进入下一轮,输家直接淘汰。每个人有一个能力值,当能力值为 $a_i$ 的人与 $a_j$ 的人对决,赢得概率是 $\frac{a_i}{a_i+a_j}$。若人数不是2的幂次,首先进行一次预选淘汰一部分人使总人数为2的幂次再进行正式比赛。现请求出一种初始对决的位置安排,使1号获胜的概率最大,输出获胜概率。
好恶心的dp
首先考虑如何安排位置能够使1号获胜概率最大。
策略:除了1之外所有人从小到大排序,若需要淘汰 $x$ 个人,那么选能力最强的 $2x$ 个人进行预选。
$proof$ :若离1号越近的人能力越强,那么在越近的回合中1号获胜的概率就越小;若能力强的人离1号远,那么在更后的回合中1号才会遇到他,而此时能力强的人坚持到这个回合会乘上一个概率,那么1号赢的概率就越大。
同时若需要预选,那么淘汰能力值强的一定是好的。
然后考虑如何得到最终的概率。
可以得到第 $k$ 轮比赛中选手 $p$ 可能会与 $2^{k-1}$ 个人进行比赛,而这 $2^{k-1}$ 个人来自离 $p$ 相邻的块 $S$ 中;设状态为 $dp[p][k]$ 表示选手 $p$ 赢得第 $k$ 轮的概率,那么有状态转移方程:
$$
dp[p][k]=dp[p][k-1]\sum_{i\in S}\frac{a[p]}{a[p]+a[i]}dp[i][k-1]
$$
初始 $dp[p][0]=1$ 。
需要注意若需要进行预选赛,那么把总人数当做 $2^x$ ,只是在进行过预选选上来的位置打上标记,若遍历到这点时,要分别计算这个位置包含的两个人的贡献。同时,需要预选的选手的 $dp[p][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 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 #include <bits/stdc++.h> using namespace std;int n;int s[5000 ];int ns[5000 ];double dp[5000 ][20 ];bool d[5000 ];int main () { cin>>n; for (int i=1 ;i<=n;i++)cin>>s[i]; sort (s+2 ,s+n+1 ); int lft=n-pow (2 ,int (log2 (n))); n=pow (2 ,int (log2 (n))); for (int i=n-lft+1 ;i<=n;i++) { d[i]=1 ; } for (int i=1 ;i<=n;i++) { if (d[i]==0 )dp[i][0 ]=1 ; else { int num=(n-lft)+(i-(n-lft))*2 -1 ; dp[num][0 ]=1.0 *s[num]/(s[num]+s[num+1 ]); dp[num+1 ][0 ]=1.0 *s[num+1 ]/(s[num]+s[num+1 ]); } } for (int k=1 ;k<=int (log2 (n));k++) { for (int p=1 ;p<=n;p++) { int x; int pw=pow (2 ,k); if (p%pw==0 ) { x=1 +(p-1 )/pw*pw; } else { int tmp1=p-p/pw*pw; if (tmp1>pw/2 ) x=1 +p/pw*pw; else x=pw/2 +1 +p/pw*pw; } for (int i=x;i<x+pow (2 ,k-1 );i++) { if (d[p]==0 && d[i]==0 ) { dp[p][k]+=dp[p][k-1 ] * s[p]/(s[p]+s[i]) * dp[i][k-1 ]; } if (d[p]==0 && d[i]!=0 ) { int num=(n-lft)+(i-(n-lft))*2 -1 ; dp[p][k]+=dp[p][k-1 ] * s[p]/(s[p]+s[num]) * dp[num][k-1 ]; dp[p][k]+=dp[p][k-1 ] * s[p]/(s[p]+s[num+1 ]) * dp[num+1 ][k-1 ]; } if (d[p]!=0 && d[i]==0 ) { int num=(n-lft)+(p-(n-lft))*2 -1 ; dp[num][k]+=dp[num][k-1 ] * s[num]/(s[num]+s[i]) * dp[i][k-1 ]; dp[num+1 ][k]+=dp[num+1 ][k-1 ] * s[num+1 ]/(s[num+1 ]+s[i]) * dp[i][k-1 ]; } if (d[p]!=0 && d[i]!=0 ) { int nump=(n-lft)+(p-(n-lft))*2 -1 ; int numi=(n-lft)+(i-(n-lft))*2 -1 ; dp[nump][k]+=dp[nump][k-1 ] * s[nump]/(s[nump]+s[numi]) * dp[numi][k-1 ]; dp[nump][k]+=dp[nump][k-1 ] * s[nump]/(s[nump]+s[numi+1 ]) * dp[numi+1 ][k-1 ]; dp[nump+1 ][k]+=dp[nump+1 ][k-1 ] * s[nump+1 ]/(s[nump+1 ]+s[numi]) * dp[numi][k-1 ]; dp[nump+1 ][k]+=dp[nump+1 ][k-1 ] * s[nump+1 ]/(s[nump+1 ]+s[numi+1 ]) * dp[numi+1 ][k-1 ]; } } } } cout<<fixed<<setprecision (15 )<<dp[1 ][int (log2 (n))]; return 0 ; }