Educational Codeforces Round 86 Rated for Div. 2
比赛链接 https://codeforces.com/contest/1342
比赛记录 https://blog.csdn.net/cheng__yu_/article/details/105395197
A. Road To Zero
题意:给定两个非负整数x、y,可以花费a元让x或者y减-1,或者花费b元让x和y同时减-1。问x=y=0时的最小花费。
思路:a的效果是1,b的效果是2,。所以2倍的a和b是等效的,如果2a>b,说明b的性价比更高。否则就只使用a
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,maxm=1e5+5;
const int mod=1e9+7,inf=0x7f7f7f7f;
int t;
ll x,y,a,b;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&x,&y);
scanf("%lld%lld",&a,&b);
if(x>y)
swap(x,y);
ll ans=0;
if(2*a<=b)
ans=(x+y)*a;
else
ans=(y-x)*a+x*b;
printf("%lld\n",ans);
}
return 0;
}
B. Binary Period(找最小周期)
题意:给定一个01串 t , 在 t 中插入一下字符0或1,使它的周期为k且k是它的最小周期
思路:如果 t 中字母全相同,周期是1。如果不相同就可以构造成010101,那么周期是2
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,maxm=1e5+5;
const int mod=1e9+7,inf=0x7f7f7f7f;
int t;
int main()
{
scanf("%d",&t);
while(t--)
{
string s;
cin>>s;
char c=s[0];
bool valid=true;
for(int i=1;i<s.length();++i)
{
if(s[i]==s[0])
continue;
valid=false;
break;
}
if(valid)
{
cout<<s<<"\n";
continue;
}
int n=s.length();
string ans="";
for(int i=1;i<=n;++i)
ans+="01";
cout<<ans<<"\n";
}
return 0;
}
C. Yet Another Counting Problem(数学找周期)
题意:找给定区间
[
l
,
r
]
[l,r]
[l,r]中
x
%
a
%
b
!
=
x
%
a
%
b
x \% a \% b != x \% a \% b
x%a%b!=x%a%b的个数。
思路一:
- 找不相等的,就去找相等的
- 找区间 [ l , r ] [l,r] [l,r] 就找 [ 1 , n ] [1,n] [1,n] 就可以了,后面可以作差
- 可以假设 a < b a<b a<b ,这样就相当于 找 x mod a == x mod b mod a,好像没什么用。
- 然后我就打了个表,发现 1 到 b - 1 都是相同的。然后一个LCM内有b个,是一个周期
- 那么[1,n]中的答案就是: n / l c m ( a , b ) + m i n ( b − 1 , n % l c m ( a , b ) ) n/lcm(a,b) + min(b-1,n\%lcm(a,b) ) n/lcm(a,b)+min(b−1,n%lcm(a,b))
思路二:
- 看到 x % a % b ! = x % a % b x \% a \% b != x \% a \% b x%a%b!=x%a%b,就应该想到 (x + ab) %a %b != (x + ab) %b %a。简单的说,就是看到这个式子的周期是 ab,所有数的结果都在 [0,ab-1]中循环
- 又因为 a、b都很小(200),所以可以在[0,ab-1]范围内统计一个f[i],表示[1,i]符合条件的个数是多少。
- 这样求n的时候,答案就是: n / a b × f [ a b − 1 ] + f [ n % a b ] n/ab\times f[ab-1]+f[n\%ab] n/ab×f[ab−1]+f[n%ab]
总结
- 两种方法都好,自己比赛中用的第一种,第二种似乎更胜一筹。
- 要是能够马上看出周期性,这题目也就可以秒了
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,maxm=1e5+5;
const int mod=1e9+7,inf=0x7f7f7f7f;
ll t,a,b,q;
ll l,r,LCM;
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
int calc(ll n)
{
int ans=n/LCM*b+min(b-1,n%LCM);
return ans;
}
int main()
{
cin>>t;
while(t--)
{
cin>>a>>b>>q;
if(a>b)
swap(a,b);
LCM=a/gcd(a,b)*b;
vector<int> ans;
while(q--)
{
cin>>l>>r;
ll res=calc(r)-calc(l-1);
res=r-l+1-res;
ans.push_back(res);
}
for(auto i : ans)
cout<<i<<" ";
cout<<"\n";
}
return 0;
}
D. Multiple Testcases(贪心)
题意:给定一个长度为n的数组m,和长度为k的数组c。希望把数组m划分成尽可能少的数组,满足每个数组中
≥
1
\ge 1
≥1 的数字个数不超过
c
1
c_1
c1,数组中
≥
2
\ge 2
≥2的数字个数不超过
c
2
c_2
c2,数组中
≥
3
\ge 3
≥3的数字个数不超过
c
3
c_3
c3
思路:题目一读完,思路就有了。
- 统计一下m数组中, ≥ 1 \ge 1 ≥1 的数字有多少个, ≥ 2 \ge 2 ≥2的数字有多少个,放到counts数组中
- 统计完后,计算 c n t = c o u n t s [ i ] / c [ i ] + ( c o u n t s [ i ] % c [ i ] ? 1 : 0 ) cnt=counts[i]/c[i] + (counts[i]\%c[i]?1:0) cnt=counts[i]/c[i]+(counts[i]%c[i]?1:0)取一个最大值
- 最后直接按顺序填到cnt个数组中就好了
- PS:比赛的时候,还在想着要怎么贪心地填才好,实际上直接填就好了
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,maxm=1e5+5;
const int mod=1e9+7,inf=0x7f7f7f7f;
int n,k;
int a[maxn],c[maxn];
int suff[maxn],counts[maxn];
int main()
{
cin>>n>>k;
for(int i=1;i<=n;++i)
cin>>a[i],counts[a[i]]++;
suff[k+1]=0;
for(int i=k;i>=1;--i)
suff[i]=counts[i]+suff[i+1];
for(int i=1;i<=k;++i)
cin>>c[i];
int cnt=0;
for(int i=1;i<=k;++i)
{
int tmp=suff[i]/c[i]+(suff[i]%c[i]?1:0);
cnt=max(cnt,tmp);
}
vector<int> ans[maxn];
int p=1;
for(int i=1;i<=k;i++)
{
while(counts[i])
{
ans[p].push_back(i);
counts[i]--;
p++;
if(p==cnt+1)
p=1;
}
}
cout<<cnt<<"\n";
for(int i=1;i<=cnt;++i)
{
cout<<ans[i].size()<<" ";
for(auto j : ans[i])
cout<<j<<" ";
puts("");
}
return 0;
}
E. Placing Rooks(容斥)
题意:在一个 n × n n\times n n×n 的棋盘上放置n个车,要求恰好有k个车能够相互攻击,并且棋盘上的每一个空位都在攻击范围内
思路:每一行或者每一列都放一个车。假设每行放一个,那么当 x x x 个车在同一列时,就能形成 x − 1 x-1 x−1 对相互攻击的车。其实可以这样想,一开始是每行每列都有一个车,每次从某一列拿掉一个车,放在另一列上时,就会形成一对相互攻击的车。
-
因此答案就是:先选择n-k行,然后将这 n n n个车放在这 n − k n-k n−k行上的方案数
C n n − k F ( n , n − k ) C_{n}^{n-k}F(n,n-k) Cnn−kF(n,n−k) -
F(n,m)是将n个不同的球放在m个不同的盒子中,无空盒的方案数。可以用容斥来写。
∣ A 1 ‾ ∩ A 2 ‾ ∩ ⋯ ∩ A m ‾ ∣ = ∣ S ∣ − ∑ ∣ A i ∣ + ∑ ∣ A i ∩ A j ∣ + ⋯ + ( − 1 ) m ∑ ∣ A 1 ∩ A 2 ∩ ⋯ ∩ A m ∣ |\overline{A_1} \cap \overline{A_2}\cap \dots \cap \overline{A_m} |=|S|-\sum{|A_i|}+\sum{|A_i \cap A_j|} \\ +\dots +(-1)^m\sum{|A_1 \cap A_2\cap \dots \cap A_m|} ∣A1∩A2∩⋯∩Am∣=∣S∣−∑∣Ai∣+∑∣Ai∩Aj∣+⋯+(−1)m∑∣A1∩A2∩⋯∩Am∣
- 这里,性质
P
1
P_1
P1 就是第一个盒子空出来,
∣
A
1
∣
|A_1|
∣A1∣ 就是第一个盒子为空的方案数
性质 P 2 P_2 P2 就是第二个盒子空出来, ∣ A 2 ∣ |A_2| ∣A2∣ 就是第二个盒子为空的方案数
∣ A i ∣ = ( m − 1 ) n |A_i|=(m-1)^n ∣Ai∣=(m−1)n、 ∣ A i ∩ A j ∣ = ( m − 2 ) n |A_i \cap A_j|=(m-2)^n ∣Ai∩Aj∣=(m−2)n - 这里的计算只与空出来的盒子数量有关,而与具体是哪个盒子无关。
∣ A 1 ‾ ∩ A 2 ‾ ∩ ⋯ ∩ A m ‾ ∣ = C m 0 ∣ S ∣ − C m 1 ∣ A i ∣ + C m 2 ∣ A i ∩ A j ∣ + ⋯ + ( − 1 ) m C m m ∣ A 1 ∩ A 2 ∩ ⋯ ∩ A m ∣ = ∑ i = 0 m ( − 1 ) m C m i ( m − i ) n |\overline{A_1} \cap \overline{A_2}\cap \dots \cap \overline{A_m} |=C_m^0|S|-C_{m}^1{|A_i|}+C_{m}^2{|A_i \cap A_j|} \\ +\dots +(-1)^mC_m^m{|A_1 \cap A_2\cap \dots \cap A_m|}\\ =\sum_{i=0}^m(-1)^mC_m^i(m-i)^n ∣A1∩A2∩⋯∩Am∣=Cm0∣S∣−Cm1∣Ai∣+Cm2∣Ai∩Aj∣+⋯+(−1)mCmm∣A1∩A2∩⋯∩Am∣=i=0∑m(−1)mCmi(m−i)n
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,maxm=1e5+5;
const int mod=998244353,inf=0x7f7f7f7f;
const int N=2e5;
int finv[N+10],fac[N+10];
int qpow(int base,int n,int mod)
{
int res=1;
while(n)
{
if(n&1)
res=1ll*res*base%mod;
base=1ll*base*base%mod;
n>>=1;
}
return res;
}
int add(int &a,int b)
{
if(a+b>mod)
a=a+b-mod;
else
a=a+b;
if(a<0)
a+=mod;
}
void init()
{
fac[0]=fac[1]=1;
for(int i=2;i<=N;++i)
fac[i]=1ll*fac[i-1]*i%mod;
finv[N]=qpow(fac[N],mod-2,mod);
for(int i=N-1;i>=0;--i)
finv[i]=1ll*finv[i+1]*(i+1)%mod;
}
int C(int n,int m)
{
return 1ll*fac[n]*finv[m]%mod*finv[n-m]%mod;
}
int F(int n,int m)
{
int ans=0;
ll x=1;
for(int i=0;i<=m;++i)
{
add(ans,x*C(m,i)*qpow(m-i,n,mod)%mod);
x=-x;
}
return ans;
}
ll n,k;
int main()
{
init();
scanf("%lld%lld",&n,&k);
int ans;
if(k==0)
ans=fac[n];
else if(k>=n)
ans=0;
else
ans=2ll*C(n,n-k)%mod*F(n,n-k)%mod;
printf("%d\n",ans);
return 0;
}