文章目录
A–Alice and Bob
题意:
总共有两堆石头,每次可以从一堆中取走
k
k
k
(
k
>
0
)
(k>0)
(k>0)块石头,从另一堆取走
s
∗
k
s*k
s∗k
(
s
>
=
0
)
(s>=0)
(s>=0)块石头, 不能操作的人输
题解:
用一个二维数组 f [ i ] [ j ] f[i][j] f[i][j] 储存状态, f [ i ] [ j ] f[i][j] f[i][j] 为 1 1 1 代表有两堆石头分别有 i , j i,j i,j 块的时候先手必胜,为 0 0 0 代表先手必败
可以发现一个状态 ( i , j ) (i,j) (i,j) 如果为先手必败态, 则上一个状态 ( i + k , j + s ∗ k ) (i+k,j+s*k) (i+k,j+s∗k) 一定为先手必胜态, 则对于每一个必败状态,枚举寻找 k , s k,s k,s
码来!
#include <bits/stdc++.h>
using namespace std;
const int N=5e3+10;
bool f[N][N];
void init()
{
for(int i=0;i<N;i++)
{
for(int j=0;j<=i;j++)
{
if(f[i][j]==0)
{
for(int k=1;k+i<N;k++)
{
for(int s=0;s*k+j<N;s++)
{
int xx=k+i;
int yy=s*k+j;
if(xx<yy) swap(xx,yy);
f[xx][yy]=1;
}
}
for(int k=1;k+j<N;k++)
{
for(int s=0;s*k+i<N;s++)
{
int xx=k+j;
int yy=s*k+i;
if(xx<yy) swap(xx,yy);
f[xx][yy]=1;
}
}
}
}
}
}
int main()
{
init();
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
if(n<m) swap(n,m);
if(f[n][m]) cout<<"Alice"<<endl;
else cout<<"Bob"<<endl;
}
return 0;
}
B–Ball Dropping
题意:
一个球体在等腰梯形中,落下请确定球是否会卡在梯形中。如果答案是“卡住”,请同时计算卡住位置(球心与底面中点之间的高度)。
题解:
如图:
可知,绿色三角形的斜边可以计算, 同时绿色三角形与黄色三角形相似, 则黄色三角形的三边可以计算得到
得到黄色三角形的斜边 y y y ,已知 z z z 部分为 b / 2 b/2 b/2, 则可计算 x x x部分的值
则以 x x x边和粉色边组成的三角形又与绿色三角形相似,可得到粉色边的值,即为答案
码来!
#include <bits/stdc++.h>
using namespace std;
int main()
{
int r,a,b,h;
cin>>r>>a>>b>>h;
if(2*r<b) cout<<"Drop"<<endl;
else
{
double s=sqrt(h*h+((a-b)/2.0)*((a-b)/2.0));
double y=r*s/h;
double x=y-b/2.0;
double ans=2.0*h*x/(a-b)*1.0;
cout<<"Stuck"<<endl;
printf("%.8f\n",ans);
}
return 0;
}
D–Determine the Photo Position
题意:
将连续 m m m 个 2 2 2 插入到矩阵中 0 0 0 的位置,有多少种方法
题解:
计算每行中连续的 0 0 0 的个数即可
码来!
#include <bits/stdc++.h>
using namespace std;
char a[2005][2005];
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
int ans=0;
string b;
cin>>b;
for(int i=0;i<n;i++)
{
int c=0;
for(int j=0;j<n;j++)
{
if(a[i][j]=='1')
{
ans+=max(0,c-m+1);
c=0;
}
else c++;
}
if(c) ans+=max(0,c-m+1);
}
cout<<ans<<endl;
return 0;
}
F–Find 3-friendly Integers
题意:
计算区间内有多少个数满足其中含有一段连续子串是 3 3 3的倍数
题解:
可以发现大于 100 100 100 的数都满足这个条件,于是先枚举 100 100 100 以内的满足条件的数字,然后计算区间内的个数
#include <bits/stdc++.h>
using namespace std;
int f[105];
int main()
{
int con=0;
for(int i=1;i<=100;i++)
{
if(i%3==0)
{
con++;
f[i]=con;
continue;
}
int j=i;
while(j)
{
int d=j%10;
if(d==0||d==3||d==6||d==9)
{
con++;
break;
}
j/=10;
}
f[i]=con;
}
int t;
cin>>t;
while(t--)
{
long long n,m;
cin>>n>>m;
long long ans1,ans2;
if(m<=100) ans2=f[m];
else ans2=76+(m-100);
if(n-1<=100) ans1=f[n-1];
else ans1=76+(n-100-1);
cout<<ans2-ans1<<endl;
}
return 0;
}
G–Game of Swapping Numbers
题意:
将
A
A
A 序列交换
k
k
k 次,使得
∑
i
=
1
n
∣
A
i
−
B
i
∣
\sum_{i=1}^n{\vert A_i-B_i \vert}
∑i=1n∣Ai−Bi∣最大
题解:
首先考虑怎样改变可以使答案变大
对于每一个 ∣ A i − B i ∣ {\vert A_i-B_i \vert} ∣Ai−Bi∣ , 去掉绝对值符号后一定有一个数字前面为负号, 一个为正号, 所以答案便是这些数字的和, 为了使答案变大, 尽量使小的数前面是负号, 大的数字前面为正号
当 n = 2 n=2 n=2 的时候,特判
当 n > 2 n>2 n>2 的时候, A A A 序列必定有至少两个数字前面符号都为 + + + 或 − - − , 假设这两个数字分别为 A i A_i Ai 和 A j A_j Aj并且前面符号都为 − - − , 则对于这两个数字和 B B B 序列对应数字的排列有两种情况
( 1 ) (1) (1) 相交
可以发现对于这种情况,交换
A
i
A_i
Ai 和
A
j
A_j
Aj,结果并不会增加
( 2 ) (2) (2) 相离
可以发现交换
A
i
A_i
Ai 和
A
j
A_j
Aj,后将会使结果增加两倍的短的红色的线的距离
即当 n > 2 n>2 n>2 的时候可以保证不会使答案结果变小,对于第二种相离的情况,可以表达为
如果 m i n ( A i , B i ) > m a x ( A j , B j ) min(A_i,B_i)>max(A_j,B_j) min(Ai,Bi)>max(Aj,Bj)
a n s + = 2 ∗ ( m i n ( A i , B i ) − m a x ( A j , B j ) ) ans+=2*(min(A_i,B_i)-max(A_j,B_j)) ans+=2∗(min(Ai,Bi)−max(Aj,Bj))
所以首先将所有的 m i n ( A i , B i ) min(A_i,B_i) min(Ai,Bi) 按照从大到小排序, 将 m a x ( A i , B i ) max(A_i,B_i) max(Ai,Bi) 按照从小到大排序, 一旦 m i n ( A i , B i ) < m a x ( A j , B j ) min(A_i,B_i)<max(A_j,B_j) min(Ai,Bi)<max(Aj,Bj) 此时交换则会将答案变小,所以此时停止交换,剩下的交换次数可以通过相交的情况使用掉
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
long long a[N],b[N],ma[N],mi[N];
int main()
{
long long n,k;
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
long long ans=0;
for(int i=1;i<=n;i++)
{
ma[i]=max(a[i],b[i]);
mi[i]=min(a[i],b[i]);
ans+=ma[i]-mi[i];
}
if(n==2)
{
if(k%2==0) cout<<ans<<endl;
else
{
ans=abs(a[1]-b[2])+abs(a[2]-b[1]);
cout<<ans<<endl;
}
}
else
{
sort(ma+1,ma+1+n);
sort(mi+1,mi+1+n,greater<long long>());
for(int i=1;i<=k&&i<=n;i++)
{
if(mi[i]>=ma[i])
{
ans+=2*(mi[i]-ma[i]);
}
else break;
}
cout<<ans<<endl;
}
return 0;
}
K–Knowledge Test about Match
题意:
给定两个序列
a a a = = = 0 , 1 , 2 , . . . . . . , n {0,1,2,......,n} 0,1,2,......,n
b b b = = = b 0 , b 1 , b 2 . . . . . . , b n − 1 b_0,b_1,b_2......,b_n-1 b0,b1,b2......,bn−1
函数
f ( a , b ) = f(a,b) = f(a,b)= ∑ i = 0 n − 1 ∣ a i − b i ∣ \sum_{i=0}^{n-1} {\sqrt{\vert a_i-b_i \vert}} ∑i=0n−1∣ai−bi∣
共 T T T 组测试数据,设第 k k k 组测试的标准答案的loss函数为 f k ∗ f{_k^*} fk∗, 你的输出结果的loss函数为 f k ^ \hat{f_k} fk^ , 则只需满足以下不等式即可:
题解:
由 x − 1 x^ {-1} x−1 图像
可以知道 a i a_i ai 和 b i b_i bi 越接近, 答案越接近标准答案, 于是使用贪心策略,
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int cnt[N],ans[N];
int main()
{
int t;
cin>>t;
while(t--)
{
memset(cnt,0,sizeof cnt);
memset(ans,-1,sizeof ans);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
cnt[x]++;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(cnt[j])
{
if(cnt[j]&&i+j<n&&ans[i+j]==-1)
{
cnt[j]--;
ans[i+j]=j;
}
if(cnt[j]&&j-i>=0&&ans[j-i]==-1)
{
cnt[j]--;
ans[j-i]=j;
}
}
}
}
for(int i=0;i<n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}