A.Berland Poker
首先算一下最多的那个人能拿多少 Joker,应该是 \(x=min(n/k,m)\) 张。那么剩下 \(m-x\) 张 joker 均分给剩下的 \(n-1\) 个人,这样可以保证第 \(2\) 多的人尽量少,所以剩下的人最多能有 \(other=(m-x)/(n-1)+(bool)((m-x)%(n-1))\) 张 joker,答案就是 \(x-other\)。
include <bits/stdc++.h>
using namespace std;
int n,m,k;
void solve(){
n=read(),m=read(),k=read();
int maxv=min(n/k,m);
int other=(m-maxv)/(k-1)+((m-maxv)%(k-1)?1:0);
printf("%d\n",maxv-other);
}
int main(){
int T=read();
while(T--) solve();
return 0;
}
B. New Theatre Square【贪心】
题意
给一个 \(n\times m\) 矩阵,有"."或"*",有两种地砖,尺寸分别为 \(1\times 1\) 和 \(1\times 2\),价格分别为 \(x\) 和 \(y\)。要求将所有 "." 覆盖,问最少需要多少钱。
思路
如果 \(x*2\le y\),那么直接用 \(1\times 1\) 的地砖铺满就好。
否则先用 \(1\times 2\) 的地砖铺,然后再用 \(1\times 1\) 的地砖将漏补上。
代码
#include <bits/stdc++.h>
using namespace std;
int n,m,x,y;
char g[110][1010];
void solve(){
n=read(),m=read(),x=read(),y=read();
for(int i=1;i<=n;i++) scanf("%s",g[i]+1);
int ans=0;
if(x*2<=y){
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)ans+=(g[i][j]=='.')*x;
return (void)printf("%d\n",ans);
}
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
if(j<m&&g[i][j]=='.'&&g[i][j+1]=='.')
ans+=y,g[i][j]=g[i][j+1]='*';
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)ans+=(g[i][j]=='.')*x;
printf("%d\n",ans);
}
int main(){
int T=read();
while(T--) solve();
return 0;
}
C. Mixing Water【数学?】
题意
给你两种温度的水,一种温度为 \(h\),一种温度为 \(c\)。你依照 \(h,c,h,c,...\) 的顺序向容器里倒水,问最少多少杯水可以使得容器中水的温度最接近 \(t\),\(h,c,t\) 满足 \(c\le t\le h\)。
思路
可以发现以这个顺序,无论倒多少杯水,最后的温度一定大于等于 \((h+c)/2\),所以如果 \(2*t\le h+c\),答案直接为 \(2\)。
否则,设要倒 \(x\) 杯 \(c\),\(x+1\) 杯 \(h\),那么当前温度为:\(\frac{(x+1)h+xc}{2x+1}=>\frac{h+c}{2}+\frac{h-c}{4x+2}\)。
解方程
得出来 \(x\) 大概是个分数,取整之后,比较 \(x\) 和 \(x+1\) 哪个对应最接近 \(t\) 的温度。然后计算答案。
代码
#include <bits/stdc++.h>
using namespace std;
int h,c,t;
void solve(){
scanf("%d%d%d",&h,&c,&t);
if(h+c>=2*t) return (void)printf("2\n");
double x=(1.0*(h-c)/(2*t-h-c)-1)/2;
int x1=(int)x,x2=x1+1,ans;
double t1=(h+c+1.0*(h-c)/(2*x1+1))/2;
double t2=(h+c+1.0*(h-c)/(2*x2+1))/2;
if(fabs(t1-t)<=fabs(t2-t)) ans=x1*2+1;
else ans=x2*2+1;
printf("%d\n",ans);
}
int main(){
int T=read();
while(T--) solve();
return 0;
}
D. Yet Another Yet Another Task【ST+二分】
题意
给数列 \(a_1,a_2,...,a_n\),计算一段连续区间之和减去该区间最大值的最大值。
思路
因为 \(a_i\) 的值域限制,可以用最大字段和的思路来做。但本菜鸡没想到,所以用了 ST+二分 的笨办法,比赛的时候还写疵了。
对于每个 \(a_i\) 找到以它为最大值的最长区间,这个显然可以用 ST表+二分 找到。假设为 L,R。那么此时要从中选出和最大且包含 \(a[i]\) 的一段的和值,用 \(s[i]\) 表示前缀和,那么这个最大的和值,就是 \(s[i],...,s[R]\) 的最大值,减去 \(s[L-1],...,s[i-1]\) 的最小值。所以这里同样可以用ST表实现。
代码
#include <bits/stdc++.h>
using namespace std;
int n,a[N],st[N][21],minv[N][21],maxv[N][21],s[N];
int smax(int l,int r){
if(l==r) return a[l];
int k=log2(r-l);
return max(st[l][k],st[r-(1<<k)][k]);
}
int maxsum(int l,int r){
if(l==r) return s[l];
int k=log2(r-l);
return max(maxv[l][k],maxv[r-(1<<k)][k]);
}
int minsum(int l,int r){
if(l==r) return s[l];
int k=log2(r-l);
return min(minv[l][k],minv[r-(1<<k)][k]);
}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read(),s[i]=s[i-1]+a[i];
for(int i=0;i<=n;i++)
st[i][0]=max(a[i],a[i+1]),
maxv[i][0]=max(s[i],s[i+1]),
minv[i][0]=min(s[i],s[i+1]);
for(int j=1;j<=20;j++)
for(int i=0;i+(1<<j)<=n;i++)
st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]),
maxv[i][j]=max(maxv[i][j-1],maxv[i+(1<<j-1)][j-1]),
minv[i][j]=min(minv[i][j-1],minv[i+(1<<j-1)][j-1]);
int ans=0;
for(int i=1,l,r,L,R;i<=n;i++){
l=1,r=i;
while(l<r){
int mid=(l+r)/2;
if(smax(mid,i)<=a[i]) r=mid;
else l=mid+1;
}
L=l,l=i,r=n;
while(l<r){
int mid=(l+r+1)/2;
if(smax(i,mid)<=a[i]) l=mid;
else r=mid-1;
}
R=l;
int temp=maxsum(i,R)-minsum(L-1,i-1)-a[i];
ans=max(ans,temp);
}
printf("%d\n",ans);
return 0;
}
E. Modular Stability
题意
问从 \(1,...,n\) 中抽 \(k\) 个数,对任意排列 \(p\),使得 \(x%a_{p_1}%a_{p_2}%...%a_{p_3}\) 相同,问这个 \(k\) 个数由多少种选择。
思路
补题的时候看到这道题,就这?比 D 题简单啊。
显然要满足条件,这 \(k\) 个数中,有 \(1\) 个数肯定是其他 \(k-1\) 个数的约数。
那么做法就很简单了,首先枚举这 \(1\) 个数,然后算出它倍数的个数 \(m\),那么就要从这这 \(m\) 个数中选出 \(k-1\) 个数,所以这 \(1\) 个数对于答案的贡献为 \(C_m^{k-1}\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5e5+10;
const int MOD=998244353;
inline LL qpow(LL x,LL k=MOD-2,LL mod=MOD){LL res=1;while(k){if(k&1) res=res*x%mod;x=x*x%mod;k>>=1;}return res;}
LL n,k,fac[N]={1};
LL C(int n,int m){
if(m>n) return 0;
return fac[n]*qpow(fac[m])%MOD*qpow(fac[n-m])%MOD;
}
int main(){
n=read(),k=read();
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
if(n<k) return printf("0\n"),0;
LL ans=0;
for(int i=1;i<=n;i++){
int num=n/i;
if(num<k) break;
ans=(ans+C(num-1,k-1))%MOD;
}
printf("%lld\n",ans);
return 0;
}
来源:oschina
链接:https://my.oschina.net/u/4311839/blog/4295601