Educational Codeforces Round 86 (Rated for Div. 2)【ABCDE】(题解)
涵盖知识点:思维、前缀和、组合数学
比赛链接:传送门
A - Road To Zero
题意: 给定\(x,y,a,b\),花费\(a\)可以使\(x,y\)其中一个加一或者减一,花费\(b\)可以使\(x,y\)同时加一或者减一。问最少花费多少使得\(x=y=0\)。
题解: 判断\(2a\)和\(b\)的大小贪心。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
int t;cin>>t;
while(t--){
int a,b,x,y;
cin>>x>>y>>a>>b;
if(x>y)swap(x,y);
cout<<1ll*min(2*a,b)*x+1ll*a*(y-x)<<"\n";
}
return 0;
}
B - Binary Period
题意: 给定01串\(t\),要求构造一个\(s\)满足\(t\)是\(s\)的子序列且\(|s|\le 2|t|\),并且拥有尽量小的周期。
题解: 若\(t\)全同:\(s=t\),否则\(s=01010101\ldots\)
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
int t;cin>>t;
while(t--){
string s;
cin>>s;
bool f[2]={false,false};
for(char i:s)f[i-'0']=true;
if(!f[0]||!f[1])cout<<s<<"\n";
else{
for(int i=0;i<s.length();i++)cout<<"01";
cout<<"\n";
}
}
return 0;
}
C - Yet Another Counting Problem
题意: 问\(i\in[l,r]\)中有多少数满足\(i\% a\%b\not =i\%b\%a\) 。
题解: 式子具有周期性。计算\([0,ab)\)区间内满足数量的前缀和处理一下就行了。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+10;
int sum[maxn];
ll a,b,q;
ll calc(ll x){
return 1ll*x/(a*b)*sum[a*b]+1ll*sum[x%(a*b)];
}
int main(){
int t;cin>>t;
while(t--){
memset(sum,0,sizeof sum);
cin>>a>>b>>q;
for(int i=1;i<=a*b;i++)sum[i]+=sum[i-1]+(i%a%b!=i%b%a);
while(q--){
ll l,r;
cin>>l>>r;
cout<<calc(r)-calc(l-1)<<" ";
}
cout<<"\n";
}
return 0;
}
D - Multiple Testcases
题意: 尽可能少的拆分给定序列,使得每个序列内大于等于\(k\)的数的个数不超过\(c_k\)。
题解: 假设原有序列大于等于\(k\)的数为\(s_k\),则\(ans=\displaystyle\max_{1\le i\le k}\{\lceil\frac{s_i}{c_i}\rceil\}\)。确定\(ans\)以后从小到大依次分配即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
int s[maxn],m[maxn],c[maxn];
vector<int> v[maxn];
int main(){
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++)cin>>m[i],s[m[i]]++;
sort(m,m+n);
for(int i=1;i<=k;i++)cin>>c[i];
for(int i=k-1;i>=1;i--)s[i]+=s[i+1];
int ans=0;
for(int i=1;i<=k;i++)ans=max(ans,(s[i]-1)/c[i]+1);
for(int i=0,j=0;j<n;i=(i+1)%ans,j++)v[i].push_back(m[j]);
cout<<ans<<"\n";
for(int i=0;i<ans;i++){
cout<<v[i].size();
for(auto j:v[i])cout<<" "<<j;
cout<<"\n";
}
return 0;
}
E - Placing Rooks
题意: 在一个\(n*n\)的棋盘上放置\(n\)个车,满足以下两个条件:
- 棋盘上的每一个空格子都能被至少一只车走到
- 恰好存在\(k\)对车可以相互攻击
询问所有车的摆放方案数
题解: 要满足第一个条件,则每一行/每一列都有一个车,两者至少满足其一。对于这两种情况是等价的,下面只考虑每一行都有车,最终结果乘\(2\)即可
在满足第二个条件的情况下,可以发现至少有\(k\)列是空着的,那么问题就相当于把\(n\)个不同的车放入\(n-k\)个不同的列,发现就是一个第二类斯特林数。那么可以用容斥原理,\(C_{n-k}^{0}*(n-k)^n-C_{n-k}^{1}*(n-k-1)^n+C_{n-k}^{2}*(n-k-2)^n...\),那么最终答案就是\(2*C_{n}^{n-k}*[C_{n-k}^{0}*(n-k)^n-C_{n-k}^{1}*(n-k-1)^n+C_{n-k}^{2}*(n-k-2)^n...]\)
要注意特判一下当\(n=0\)时,答案就为\(n!\),当\(k\ge n\)时,答案为\(0\)
第二类斯特林数详解传送门
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
const int mod = 998244353;
ll fac[maxn];
ll qpow(ll a, ll b){
ll res = 1;
while (b){
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
void init(){
fac[0] = 1;
for (int i = 1; i < maxn; i++)
fac[i] = fac[i - 1] * i % mod;
}
ll c(int x, int y){
return fac[x] * qpow(fac[y] * fac[x - y] % mod, mod - 2) % mod;
}
int main(){
init();
int n, k;
cin>>n>>k;
if (k == 0){
cout<<fac[n]<<"\n";
return 0;
}
if (k >= n){
cout<<"0\n";
return 0;
}
ll ans = 0;
for (int i = 0; i <= n - k; i++)
ans += 1ll * qpow(mod - 1, i) * c(n - k, i) % mod * qpow(n - k - i, n) % mod;
ans %= mod;
cout<<2ll * ans % mod * c(n, n - k) % mod<<"\n";
return 0;
}