题目链接:Dashboard - The 2021 Shanghai Collegiate Programming Contest - Codeforces
前言:剩余题目待补充
A - 小A的点面论
算法:几何知识
题解:答案为两个向量的叉积,(三维空间中,两个向量的叉积就是这两个向量确定平面的法向量) 可直接利用公式,也可以暴力枚举计算。
代码一:时间复杂度O(1)
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int N = 2e3 + 100;
struct node
{
int x, y, z;
}s[N];
int main()
{
IOS;
cin>>s[1].x>>s[1].y>>s[1].z>>s[2].x>>s[2].y>>s[2].z;
cout<<s[1].y*s[2].z-s[1].z*s[2].y<<" ";
cout<<s[1].z*s[2].x-s[1].x*s[2].z<<" ";
cout<<s[1].x*s[2].y-s[1].y*s[2].x;
return 0;
}
代码二:时间复杂度O()
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int N = 2e3 + 100;
struct node
{
int x, y, z;
}s[N];
int main()
{
IOS;
cin>>s[1].x>>s[1].y>>s[1].z>>s[2].x>>s[2].y>>s[2].z;
for(int i = -200; i <= 200; i++)
{
for(int j = -200; j <= 200; j++)
{
for(int k = -200; k <= 200; k++)
{
if(i*s[1].x + j*s[1].y + k*s[1].z == 0 && i*s[2].x + j*s[2].y + k*s[2].z == 0)
{
cout<<i<<" "<<j<<" "<<k;
return 0;
}
}
}
}
return 0;
}
B - 小A的卡牌游戏
算法:dp
题解:如果题目简化为两组数a和b,则可按照b-a 降序排序,然后贪心地先选择b再选择a。基于此贪心思想,对本题使用dp,dp[i][j]表示在前i次选择中,有j次选择第三组卡牌c,剩余i-j次按照前面贪心思想选择剩余两组卡牌。每次状态转移时,比较前后两种状态(本次是否选择该组卡牌),取max即可。注意dp数组需初始化,最大值为-1e9,可更小。
代码:时间复杂度O()
typedef long long ll;
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int N = 5e3 + 100;
int n, A, B, C;
struct node
{
int a, b, c;
bool operator < (const node &y) const
{
if(b-a == y.b-y.a) return c > y.c;
return b-a > y.b-y.a;
}
}v[15010];
ll dp[N][N];
int main()
{
IOS;
cin>>n>>A>>B>>C;
for(int i = 1; i <= n; i++) cin>>v[i].a>>v[i].b>>v[i].c;
sort(v+1, v+n+1);
for(int i = 0; i <= n; i++)
for(int j = i+1; j <= C; j++)
dp[i][j] = -1e9;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= min(C, i); j++)
{
if(j) dp[i][j] = max(dp[i][j], dp[i-1][j-1] + v[i].c);
if(i-j <= B) dp[i][j] = max(dp[i][j], dp[i-1][j] + v[i].b);
else dp[i][j] = max(dp[i][j], dp[i-1][j] + v[i].a);
}
}
cout<<dp[n][C];
return 0;
}
C - 小A的期末考试
算法:模拟
题解:简单模拟,注意输出的格式。
代码:时间复杂度O(n)
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const double eps = 1e-6;
const int N = 2e5 + 100;
int n, m, sum;
double mid;
struct node
{
int a, b;
bool operator < (const node &y) const
{
return a < y.a;
}
}s[N];
int main()
{
IOS;
cin>>n>>m;
for(int i = 1; i <= n; i++)
{
cin>>s[i].a>>s[i].b;
sum += s[i].b;
}
mid = 1.0*sum/n;
for(int i = 1; i <= n; i++)
{
if(s[i].a == m)
{
if(s[i].b < 60) s[i].b = 60;
}
else
{
if(1.0*s[i].b >= mid + eps) s[i].b -= 2;
if(s[i].b < 0) s[i].b = 0;
}
}
sort(s+1, s+n+1);
for(int i = 1; i <= n-1; i++) cout<<s[i].b<<" ";
cout<<s[n].b;
return 0;
}
D - Zztrans的班级合照
算法:dp
题解:dp[i][j]表示两排共选择了i个人,第一排比第二排多j个人。使用数组cnt存储相同身高的人数,将身高升序排序,去重。第一层循环表示选择身高为a[i]的学生,第二层循环表示目前第一排比第二排多j个学生,第三层循环表示在身高为a[i]的学生中选择k个放在第二排,剩余cnt[a[i]]-k个放在第一排。(这k个学生放第一排或者放第二排都可,只是状态转移方程有一些不同)
假设之前第一排有x+j个学生,第二排有x个学生,则放置k个学生之后,第一排学生总数变为x+j+cnt[a[i]]-k,第二排变为x+k,此时第一排和第二排的学生数量差值为now = j+cnt[a[i]]-2*k,当差值大于0的时候才是有效转移。所以现在的状态dp[i][now]可以从上次的的状态dp[i-1][j]转移,由于选择的是身高为a[i]的一类学生,学生们身高一样但代表着的还是不同的人,所以需要乘以该身高学生个数的阶乘。(阶乘在开始预处理并存储在数组里面)
代码:时间复杂度O()
typedef long long ll;
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int mod = 998244353;
const int N = 5e3 + 100;
int n;
ll a[N], cnt[N], dp[N][N], fac[N];
void init_fac()
{
fac[1] = 1;
for(int i = 2; i <= N; i++) (fac[i] = fac[i-1]*i)%=mod;
}
int main()
{
IOS;
init_fac();
cin>>n;
for(int i = 1; i <= n; i++)
{
cin>>a[i];
cnt[a[i]]++;
}
sort(a+1, a+n+1);
int nn = unique(a+1, a+n+1)-(a+1);
dp[0][0] = 1;
for(int i = 1; i <= nn; i++)
{
for(int j = 0; j <= n; j++)
{
for(int k = 0; k <= cnt[a[i]]; k++)
{
int now = j+cnt[a[i]]-2*k;
if(now < 0) break;
(dp[i][now] += dp[i-1][j]*fac[cnt[a[i]]])%=mod;
}
}
}
cout<<dp[nn][0];
return 0;
}
E - Zztrans的庄园
算法:概率与期望
题解:基本期望知识。期望定义式:
代码:时间复杂度:O(n)
#define IOS ios::sync_with_stdio(0);cin.tie(0);
int n, k;
double sum;
int main()
{
// IOS;
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; i++)
{
char s; double x; cin>>s>>x;
if(s == 'A') x *= 57;
if(s == 'B') x *= 31;
if(s == 'C') x *= 1;
if(s == 'D') x *= -7;
if(s == 'S') x *= 9977;
sum += x;
}
printf("%.4f", sum*k);
return 0;
}
G - 鸡哥的雕像
算法:逆元
题解:每个位置的答案就是所有的数的乘积除以该位置上的数字并对998244353取模,使用逆元计算sum/a[i],即sum*ksm(a[i], mod-2)(ksm为快速幂函数)。但由于998244353 < 1e9。所以分三种情况:给出的数中有两个及以上的998244353,这样的话所有的位置最终结果都为0;给出的数中有一位是998244353,则只有这一位上的结果为其它为上数的乘积,其他位全为0;没有出现998244353,则按照正常公式计算。
代码:时间复杂度O(n)
typedef long long ll;
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int mod = 998244353;
const int N = 1e5 + 100;
ll ksm(ll a, ll b)
{
ll res = 1;
while(b)
{
if(b&1) (res *= a)%=mod;
(a *= a)%=mod;
b>>=1;
}
return res;
}
ll n, cnt;
ll a[N];
int vis[N];
int main()
{
IOS;
ll sum = 1;
cin>>n;
for(int i = 1; i <= n; i++)
{
cin>>a[i];
if(a[i] == mod) vis[i] = 1, cnt++;
else (sum *= a[i])%=mod;
}
if(cnt >= 2)
{
for(int i = 1; i <= n-1; i++) cout<<0<<" ";
cout<<0;
}
else if(cnt == 1)
{
for(int i = 1; i <= n-1; i++)
{
if(vis[i]) cout<<sum<<" ";
else cout<<0<<" ";
}
if(vis[n]) cout<<sum;
else cout<<0;
}
else
{
for(int i = 1; i <= n-1 ;i++) cout<<sum*ksm(a[i], mod-2)%mod<<" ";
cout<<sum*ksm(a[n], mod-2)%mod;
}
return 0;
}
J - Alice and Bob 1
算法:思维
题解:若数据全为正值:在轮流取卡片的过程中,Alice拿当前最大的,Bob取第二大的即可保证Alice取得的为最大值。但由于题目最后的结果为绝对值且所给数据中有负值,所以在数据按递减排序后,会有Alice从前往后取,先取正值再取负值综合为正值,和从后往前取,先取负值再取正值总和为负值,这两种结果的绝对值都有可能是最大值。
例如第一组数据: 5 3 2 1 -1 -2 这种情况下Alice要从前往后取。第二组数据:1 2 -3 -5 -7 -8这种情况下Alice要从后往前取。
代码:时间复杂度O(n)
typedef long long ll;
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int N = 5e3 + 100;
int n;
ll sum1, sum2, sum;
int a[N];
bool cmp(int x, int y)
{
return x > y;
}
int main()
{
IOS;
cin>>n;
for(int i = 1; i <= n; i++) cin>>a[i], sum += a[i];
sort(a+1, a+n+1, cmp);
for(int i = 1; i <= n; i += 2) sum1 += a[i];
for(int i = n; i >= 1; i -= 2) sum2 += a[i];
if(abs(sum1) > abs(sum2)) cout<<(ll)abs(sum1) - (ll)abs(sum-sum1);
else cout<<(ll)abs(sum2) - (ll)abs(sum-sum2);
return 0;
}