第16届程序设计上海金马五校赛

第16届程序设计上海金马五校赛

A

描述
最近对抗生成网络(GAN)很火,其中有一种变体WGAN,引入了一种新的距离来提高生成图片的质量。这个距离就是Wasserstein距离,又名铲土距离。
这个问题可以描述如下:
有两堆泥土,每一堆有n个位置,标号从1~n。第一堆泥土的第i个位置有ai克泥土,第二堆泥土的第i个位置有bi克泥土。小埃可以在第一堆泥土中任意移挪动泥土,具体地从第i个位置移动k克泥土到第j个位置,但是会消耗k×|i-j|的体力。小埃的最终目的是通过在第一堆中挪动泥土,使得第一堆泥土最终的形态和第二堆相同,也就是ai=bi (1<=i<=n), 但是要求所花费的体力最小
输入测试组数T,每组测试数据,第一行输入n,1<=n<=100000,紧接着输入两行,每行n个整数,前一行为a1, a2,…,an,后一行为b1,b2,…,bn.其中0<=ai,bi<=100000,1<=i<=n

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<bits/stdc++.h>
#define maxn 100050
#define LL long long
using namespace std;
int t,n;
int a[maxn],b[maxn];
int main(){
// freopen("input.txt","r",stdin);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
LL ans=0;
for(int i=1;i<=n;i++){
if(a[i]==b[i]) continue;
int tmp=abs(a[i]-b[i]);
if(a[i]>b[i]){
for(int j=i+1;j<=n;j++){
if(a[j]==b[j]) continue;
else{
a[j]+=tmp;
ans+=(LL)tmp*(j-i);
break;
}
}
}
else{
for(int j=i+1;j<=n;j++){
if(a[j]==b[j]) continue;
else{
if(a[j]>=tmp){
a[j]-=tmp;
ans+=(LL)tmp*(j-i);
break;
}
else{
tmp-=a[j];
ans+=(LL)a[j]*(j-i);
a[j]=0;
}
}
}
}
}
cout<<ans<<endl;
}
return 0;
}

思路

直接模拟 从左往后扫描 多了放到右边 不够从右边拿

F

描述
小Y在研究数字的时候,发现了一个神奇的等式方程x^2x=3x,他屈指算了一下有很多正整数x满足这个等式,比如1和2,现在问题来了,他想知道从小到大第N个满足这个等式的正整数,请你用程序帮他计算一下。
第一行是一个正整数T,表示查询次数。
接着有T行,每行有一个正整数n(n<10^12),表示小Y的查询

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int t;
LL f[70],g[70];
int main(){
f[1]=1,f[2]=2;
g[1]=1,g[2]=2;
for(int i=3;i<=60;i++){
g[i]=g[i-1]*2;
f[i]=f[i-1]+f[i-2];
}
cin>>t;
while(t--){
LL n;
cin>>n;
LL ans=0;
for(int i=60;i>=1;i--){
if(n>=f[i]){
ans+=g[i];
n-=f[i];
}
}
cout<<ans<<endl;
}
}

思路

打表发现规律跟二进制有关 每位二进制个数的数量满足斐波拉契数列
f[i]表示二进制长度小于i-1前面所有长度的个数和 这时候答案加上的是g[i]代表一个二进制数
对于一个数n 我们根据规律从二进制头开始找1 相当于加到该数字 找到后ans+某个二进制数

I

描述
我们把十进制下每一位都是偶数的数字叫做“二数”。
小埃表示自己很聪明,最近他不仅能够从小数到大:2,3,4,5….,也学会了从大数到小:100,99,98…,他想知道从一个数开始数最少的数就得到一个二数。但是聪明的小森已经偷偷在心里算好了小埃会数到哪个二数,请你求出他要数到哪个数吧。
换句话说,给定一个十进制下最多105位的数字,请你求出和这个数字的差的绝对值最小的二数,若答案不唯一,输出最小的那个。
也就是说,给定数字n,求出m,使得abs(n-m)最小且m[i] mod 2 = 0
1 ≤ T ≤ 100, 1 ≤ n ≤ 10^100000 − 1, T组数据的数字的十进制表示长度总和不超过1000000

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<bits/stdc++.h>
using namespace std;
int t,len;
string s;
int check(int x){
int num=s[x]-'0';
if(num<4) return -1;
else if(num>4) return 1;
else if(x==len-1 && num==4) return -1;
return check(x+1);
}
int main(){
// freopen("input.txt","r",stdin);
cin>>t;
while(t--){
cin>>s;
len=s.length();
int up=0;
for(int i=0;i<len-1;i++){
int num=s[i]-'0';
if(num&1){
if(num==9) up=-1;
else up=check(i+1);
if(up==-1){
s[i]=num-1+'0';
for(int j=i+1;j<len-1;j++) s[j]='8';
break;
}
else if(up==1){
s[i]=num+1+'0';
for(int j=i+1;j<len-1;j++) s[j]='0';
break;
}
}
}
if(len==1){
int num=s[0]-'0';
if(num&1) num--;
cout<<num<<endl;
continue;
}
if(up==0){
int num=s[len-1]-'0';
if(num&1) s[len-1]=num-1+'0';
for(int i=0;i<len;i++) cout<<s[i];
cout<<endl;
continue;
}
if(up==1) s[len-1]='0';
else if(up==-1) s[len-1]='8';
int flag=1;
for(int i=0;i<len;i++){
if(s[i]=='0' && flag) continue;
flag=0;
cout<<s[i];
}
cout<<endl;
}
return 0;
}

思路

贪心
边界在于4 少于4下降 大于4上升 若等于4 比如34的up为40 down为28 差值相等 所以继续判断下一位
若最后一位还相等 说明存在多组答案 那么选择down 因为题目要求最小
找到第一个奇数位置 判断是up还是down up后后面各位全部变成0 down后后面各位全部变成8
有多个hack点:例如0-9的数字要特判是奇还是偶 up=0代表前len-1位没有奇数 那么特判最后一位是奇还是偶
除去前两种情况后以为根据up还是down更新最后一位 输出要删去前导0

J

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<bits/stdc++.h>
#define mm(a,b) memset(a,b,sizeof a)
#define maxn 250
using namespace std;
int t,n,m,mid;
int a[maxn],b[maxn];
int g[maxn][maxn],linker[maxn];
bool used[maxn];
void init(){
mm(g,0);
for(int i=1;i<=m;i++){
for(int j=1;j<n;j++){
g[i][j]=max(abs(b[i]-a[j]),abs(b[i]-a[j+1]));
}
g[i][0]=abs(b[i]-a[1]);
g[i][n]=abs(b[i]-a[n]);
}
for(int i=m+1;i<=n+1;i++){
for(int j=1;j<n;j++){
g[i][j]=abs(a[j]-a[j+1]);
}
}
}
bool dfs(int u){
for(int i=0;i<=n;i++){
if(g[u][i]<=mid && !used[i]){
used[i]=true;
if(linker[i]==-1 || dfs(linker[i])){
linker[i]=u;
return true;
}
}
}
return false;
}
int hungary(){
int ans=0;
mm(linker,-1);
for(int i=1;i<=n+1;i++){
mm(used,0);
if(dfs(i)) ans++;
}
return ans;
}
int main(){
// freopen("input.txt","r",stdin);
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d",&b[i]);
init();
int l=0,r=1e9,ans=0;
while(l<=r){
mid=l+r>>1;
int tmp=hungary();
if(tmp==n+1){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%d\n",ans);
}
return 0;
}

思路

二分+匹配 二分最小不连贯值
n个广告有n+1个坑位 这n+1个坑位与m个广告匹配 再新建n+m-1个假广告位与n匹配 相当于这个坑位没有新广告进入
hungary跑出来的匹配如果是n+1 即全部匹配 修改二分边界

L

描述
给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”。现在要你 对数组 a 求出最长的子序列的长度,满足这个序列是 K
第一行为两个整数 n, K, 以空格分隔,第二行为 n 个整数,表示 a[1] ∼ a[n],1 ≤ n ≤ 10^5 , 1 ≤ a[i] ≤ 10^9 , 1 ≤ nK ≤ 10^7

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<bits/stdc++.h>
#define maxn 100050
using namespace std;
int n,k,a[maxn];
int dp[10000000][2];
int main(){
// freopen("input.txt","r",stdin);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]%=k;
}
memset(dp,0,sizeof dp);
dp[a[1]][0]=1;
int now=0;
for(int i=2;i<=n;i++){
now=1-now;
for(int j=0;j<k;j++){
if(dp[((k+j)-a[i])%k][1-now]) dp[j][now]=max(dp[j][1-now],dp[((k+j)-a[i])%k][1-now]+1);
else dp[j][now]=dp[j][1-now];
}
}
cout<<dp[0][now]<<endl;
return 0;
}

思路

dp 因为nk小于10^7 所以数组不能开dp[n][k] 改为滚动数组
dp的第一位代表余数 最后输出的第一位为0代表mod k等于0 即能被k整除
dp方程:若dp[x][now]由1-now推来 x的余数有两种来源 一种是dp[x][1-now] 还有一种是由该位置和前一位置的某个余数相加mod k后等于x(前提是那个数字存在) 取max即可

文章目录
  1. 1. 第16届程序设计上海金马五校赛
    1. 1.1. A
      1. 1.1.1. 参考AC代码
      2. 1.1.2. 思路
    2. 1.2. F
      1. 1.2.1. 参考AC代码
      2. 1.2.2. 思路
    3. 1.3. I
      1. 1.3.1. 参考AC代码
      2. 1.3.2. 思路
    4. 1.4. J
      1. 1.4.1. 参考AC代码
      2. 1.4.2. 思路
    5. 1.5. L
      1. 1.5.1. 参考AC代码
      2. 1.5.2. 思路
|