同济校赛(牛客5.21)
A.盒饭盲盒
- 题解
排列组合的定义题,分别计算全荤的事件数量以及总事件数量,注意全素是不会发生的需要剔除,计算下面式子代表的概率后,gcd计算最简分数形式
p
=
(
m
−
a
)
3
m
3
−
a
3
=
(
m
−
a
)
2
m
2
+
m
a
+
a
2
p=\frac{(m-a)^3}{m^3-a^3}=\frac{(m-a)^2}{m^2+ma+a^2}
p=m3−a3(m−a)3=m2+ma+a2(m−a)2
- 代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
LL gcd(LL a, LL b) // 欧几里得算法
{
return b ? gcd(b, a % b) : a;
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
LL m,a,b,c;
scanf("%lld%lld", &m, &a);
b=m-a;b*=b;
c=m*a;
m*=m;
a*=a;
LL d=gcd(b,m+a+c);
printf("%lld/%lld\n",b/d,(m+a+c)/d);
}
return 0;
}
K.乐观的R家族
- 题解
同一题某答案选的人越多总分和越大,故只需统计每题选的人最多答案乘分值最后相加
- 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n,m;
char s[N][N];
int a[N];
int main()
{
cin>>n>>m;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
cin>>s[i][j];
for (int i = 0; i < m; i ++ )cin>>a[i];
long long res=0;//防爆int
for (int i = 0; i < m; i ++ ){//遍历每列,即看每一题的情况
int cnt[5]={0};//哈希计数
int m=-1;
for (int j = 0; j < n; j ++ ){//统计每个答案的数量
cnt[s[j][i]-65]++;
}
for (int j = 0; j < 5; j ++ ){//找选择最多的
m=max(m,cnt[j]);
}
res+=m*a[i];//当前题最大分总和
}
cout << res<<'\n';
return 0;
}
C. 攻城
-
题解
题目可以转化为每6次单点攻击后进行一次群体攻击,且(n!=1/0)最后一次群体攻击时的血量分布全为1。因此可以推出两个必要条件:
1.血量和 sum % (6+n) == 0;
2.最小血量塔 min>=(sum/(6+n)),即最小血量塔要能承受应有的群体攻击
-
代码
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
int main()
{
int t;
scanf("%d", &t);
while (t -- ){
int n,x,mi=0x3f3f3f3f;
scanf("%d", &n);
LL sum=0;
for (int i = 0; i < n; i ++ ){
scanf("%d", &x);
sum+=x;
mi=min(mi,x);
}
if(n==0 || n==1){printf("YES\n");continue;}//特判0,1
int flag=0;
if(sum%(6+n)==0 && mi>=(sum/(6+n)))flag=1;
if(flag)printf("YES\n");
else printf("NO\n");
}
return 0;
}
D.两串糖果
-
题解
其实一眼区间dp,dp[i]表示0~i中通过各种交换后的最大值,v[i] [j]为不变换的区间乘积和,rev[i] [j]为区间反转后的区间乘积和。状态转移方程:
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + m a x ( v [ j + 1 ] [ i ] , r e v [ j + 1 ] [ i ] ) ) dp[i]=max(dp[i],dp[j]+max(v[j+1][i],rev[j+1][i])) dp[i]=max(dp[i],dp[j]+max(v[j+1][i],rev[j+1][i]))
v [ i ] [ j ] = v [ i + 1 ] [ j − 1 ] + a [ i ] ∗ b [ i ] + a [ j ] ∗ b [ j ] v[i][j]=v[i+1][j-1]+a[i]*b[i]+a[j]*b[j] v[i][j]=v[i+1][j−1]+a[i]∗b[i]+a[j]∗b[j]
r e v [ i ] [ j ] = r e v [ i + 1 ] [ j − 1 ] + a [ i ] ∗ b [ j ] + a [ j ] ∗ b [ i ] rev[i][j]=rev[i+1][j-1]+a[i]*b[j]+a[j]*b[i] rev[i][j]=rev[i+1][j−1]+a[i]∗b[j]+a[j]∗b[i]
解题过程:
- 预处理v[n] [n]和rev[n] [n],从上述v,rev的转移方程知必须先把长度为1和2的区间最先预处理出来
- 区间dp,注意第二重循环从0开始
- 代码
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 5005;
int n,a[N],b[N];
LL v[N][N],rev[N][N],f[N];
int main()
{
cin>>n;
for (int i = 1; i <= n; i ++ )cin>>a[i];
for (int i = 1; i <= n; i ++ )cin>>b[i];
for (int i = 1; i <= n; i ++ ){v[i][i]=a[i]*b[i];rev[i][i]=v[i][i];}//长度为1预处理
for (int i = 1; i < n; i ++ )//长度为2的预处理
{
v[i][i+1]=v[i][i]+v[i+1][i+1];
rev[i][i+1]=a[i]*b[i+1]+a[i+1]*b[i];
}
for (int j = 1; j <= n; j ++ )//预处理v,rev
for (int i = 1; i < j-1; i ++ ){
v[i][j]=v[i+1][j-1]+a[i]*b[i]+a[j]*b[j];
rev[i][j]=rev[i+1][j-1]+a[i]*b[j]+a[j]*b[i];
}
for (int i = 1; i <= n; i ++ )//区间dp
for (int j = 0; j < i; j ++ )
f[i]=max(f[i],f[j]+max(v[j+1][i],rev[j+1][i]));
cout<<f[n]<<'\n';
return 0;
}