2020.10.29【CSP-J/S】普及组模拟赛总结
题号 | 题目 |
---|---|
T1 | 捡石头 |
T2 | 魔法药水 |
T3 | 土地恢复 |
T4 | 组合数 |
T5 | 排数字 |
T6 | 小武的方程 |
得分 | 443/600 |
T1
思路
我们先用桶来记录每个石子的位置,然后分类讨论:
- 小武走到第一个,小林走到第二个
- 小武走到第二个,小林走到第一个
如图:
那么转移方程就是:
a n s + = m i n ( a b s ( a [ i + 1 ] [ 1 ] − a [ i ] [ 1 ] ) + a b s ( a [ i + 1 ] [ 2 ] − a [ i ] [ 2 ] ) , ans+=min(abs(a[i+1][1]-a[i][1])+abs(a[i+1][2]-a[i][2]), ans+=min(abs(a[i+1][1]−a[i][1])+abs(a[i+1][2]−a[i][2]),
a b s ( a [ i + 1 ] [ 2 ] − a [ i ] [ 1 ] ) + a b s ( a [ i + 1 ] [ 1 ] − a [ i ] [ 2 ] ) ) abs(a[i+1][2]-a[i][1])+abs(a[i+1][1]-a[i][2])) abs(a[i+1][2]−a[i][1])+abs(a[i+1][1]−a[i][2]))
代码
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long a[100010][2],b[100010];
long long n,s,ans;
int main()
{
cin>>n;
for(int i=1; i<=2*n; i++)
{
scanf("%d",&s);
if(b[s]==1)
{
b[s]++;
a[s][1]=i;
}
else
a[s][0]=i,b[s]++;
}
ans=a[1][0]+a[1][1]-2;
for(int i=1; i<n; i++)
ans=ans+min(abs(a[i+1][0]-a[i][0])+abs(a[i+1][1]-a[i][1]),abs(a[i+1][0]-a[i][1])+abs(a[i+1][1]-a[i][0]));
cout<<ans;
return 0;
}
T2
思路
首先统计出每个
L
i
L_i
Li 的个数。
因为
2
x
+
2
x
=
2
x
+
1
2^x+2^x=2^{x+1}
2x+2x=2x+1
所以我们可以不断合并每个
L
i
L_i
Li,
当
L
i
L_i
Li 个数是奇数时,合并
L
i
−
1
L_i-1
Li−1 个,将剩下的那个
L
i
L_i
Li 用一个瓶子装。
当
L
i
L_i
Li 个数是偶数时,合并
L
i
L_i
Li 个。
代码
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long x,a[1001010];
long long n,ans;
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
{
scanf("%d",&x);
a[x]++;
}
for(int i=0; i<=1001008; i++)
{
a[i+1]+=a[i]/2;
ans=ans+a[i]%2;
}
cout<<ans;
return 0;
}
T3
思路
本题可以直接暴力查询。
每次从一个完整的区间里选一个最小的数(完整的意思是没有零的区间),
并减去次数,统计答案,直到数列全部变成零。
代码
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,j=1,k,minn=2147483647,ans;
int a[100010];
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
while(j<=n)
{
if(a[j]==0)
{
j++;
continue;
}
k=j;
minn=2147483647;
while(a[k]>0)
{
minn=min(a[k],minn);
k++;
}
k=j;
while(a[k]>0)
{
a[k]-=minn;
k++;
}
ans+=minn;
}
cout<<ans;
return 0;
}
T4
思路
此题是原题,具体解释参考这篇博客(组合数问题)。
代码
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long sum[2510][2510],yh[2510][2510];
long long t,k,n,m;
void yhsj()
{
yh[0][0]=1,yh[1][0]=1,yh[1][1]=1;
for(int i=2; i<=2000; i++)
{
yh[i][0]=1;
for(int j=1; j<=i; j++)
{
yh[i][j]=(yh[i-1][j-1]+yh[i-1][j])%k;
if(yh[i][j]==0)
sum[i][j]=1;
sum[i][j]=sum[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
}
sum[i][i+1]=sum[i][i];
}
}
int main()
{
cin>>t>>k;
yhsj();
while(t--)
{
cin>>n>>m;
if(m>n)
cout<<sum[n][n]<<endl;
else
cout<<sum[n][m]<<endl;
}
return 0;
}
T5
思路
用贪心的思想,每次找到一个没选的数,
看以它能不能找出
m
m
m 个数满足连续需求,如果不能就直接
f
a
l
s
e
false
false。
代码
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long T,n,m,bj,mm,k,w,x;
long long a[100010];
int main()
{
cin>>T;
while(T--)
{
w=0;
cin>>n>>m;
if(n%m!=0)
{
cout<<"false"<<endl;
continue;
}
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
sort(a+1,a+1+n);
for(int i=1; i<=n/m; i++)
{
for(int j=1; j<=n; j++)
if(a[j]>=0)
{
bj=a[j];
a[j]=-1;
break;
}
mm=1,k=0,x=1;
while(mm<m&&k<=n)
{
k++;
if(a[k]==-1||a[k]==bj)
continue;
if(a[k]==bj+x)
a[k]=-1,mm++,x++;
}
if(k>n&&mm<m)
{
cout<<"false"<<endl;
w=1;
break;
}
}
if(w==1)
continue;
for(int j=1; j<=n; j++)
if(a[j]!=-1)
{
cout<<"false"<<endl;
w=1;
continue;
}
if(w==0)
cout<<"true"<<endl;
}
return 0;
}
T6
思路
首先,因为
x
∣
y
x|y
x∣y 一定小于
x
+
y
x+y
x+y,所以必须满足
B
>
=
A
B>=A
B>=A 的情况。
其次,我们让
x
x
x 等于
A
A
A,那么
B
−
A
B-A
B−A 就是
y
y
y 的值(在满足第二个条件的情况下)
这时就让
A
A
A 和
B
−
A
B-A
B−A 异或一下,看等不等与
A
A
A 就可以了。
代码
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long t,a,b;
int main()
{
cin>>t;
while(t--)
{
cin>>a>>b;
if((b>=a)&&(((b-a)|a)==a))
cout<<"Possible"<<endl;
else
cout<<"Impossible"<<endl;
}
return 0;
}
上一篇: 2020牛客暑期多校训练营(第二场)F题