第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛

A Wasserstein Distance

题目描述

最近对抗生成网络(GAN)很火,其中有一种变体WGAN,引入了一种新的距离来提高生成图片的质量。这个距离就是Wasserstein距离,又名铲土距离。
这个问题可以描述如下:


有两堆泥土,每一堆有n个位置,标号从1~n。第一堆泥土的第i个位置有ai克泥土,第二堆泥土的第i个位置有bi克泥土。小埃可以在第一堆泥土中任意移挪动泥土,具体地从第i个位置移动k克泥土到第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,数据保证 

输出描述:

对于每组数据,输出一行,将a土堆的形态变成b土堆的形态所需要花费的最小体力
示例1

输入

2
3
0 0 9
0 2 7
3
1 7 6
6 6 2

输出

2
9

备注:

输入数据量较大,建议使用scanf/printf

从左到右扫描,遇见ai小于bi就从ai的后面最近的拿,大于的话就把多余的放在ai+1上。

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <cstring>
 4 #include <cmath>
 5 #define ll long long
 6 using namespace std;
 7 const int N = 100010;
 8 ll a[N], b[N];
 9 int main() {
10     ll t, n;
11     scanf("%lld",&t);
12     while(t--) {
13         scanf("%lld",&n);
14         ll ans = 0;
15         for(int i = 1; i <= n; i ++) scanf("%lld",&a[i]);
16         for(int i = 1; i <= n; i ++) scanf("%lld",&b[i]);
17         for(ll i = 1; i <= n; i ++) {
18             if(a[i] == b[i])continue;
19             if(a[i] > b[i]) {
20                 a[i+1] += a[i] - b[i];
21                 ans += a[i] - b[i];
22             //  printf("1:%d\n",ans);
23             }
24             if(a[i] < b[i]) {
25                 ll tmp = b[i]-a[i];
26                 for(ll j = i+1; j <= n; j ++) {
27                     if(tmp >= a[j]) {
28                         ans += abs(j-i)*a[j];
29                         //printf("2:%d\n",ans);
30                         tmp -= a[j];
31                         a[j] = 0;
32                     } else{
33                         ans += (j-i)*tmp;
34                         //printf("3:%d\n",ans);
35                         a[j] -= tmp;
36                         break;
37                     }
38                 }
39             }
40         }
41         cout << ans << endl;
42     }
43     return 0;
44 }

 

 

B 合约数点击这里

 

D 数字游戏

题目描述

小埃和小森在玩一个数字游戏,小埃先从区间[L1, R1]里选择1个数字n1,小森看到小埃选的数字后,从[L2,R2]里选择1个数字n2, 将n1和n2连接在一起(n1在前, n2在后),形成一个新的数字,若这个数字可以被mod整除,那么小森获胜,否则小埃获胜。若两个人均采取最优策略,试问谁获胜?

输入描述:

输入测试组数T,每组数据,输入一行整数L1, R1, L2, R2, mod,其中1<=L1<=R1<109,1<=L2<=R2<109, 1<=mod<=106

输出描述:

每组数据输出一行,若小埃获胜,输出WIN,否则输出LOSE
示例1

输入

2
6 9 3 5 1
5 10 7 8 6

输出

LOSE
WIN

这个看能不能%mod等于0关键看n2。
如果[l2,r2]的区间小于mod的话,假设在[l1,r2]之中选择x,那么组合的数区间为[xl2,xr2] 中会有一个数使的这个区间%mod不等于0。
如果[l2,r2]的区间大于mod的话,组合的区间为[xl2,xr2],一定会存在一个数%mod等于0。
 1 #include <iostream>
 2 using namespace std;
 3 
 4 int main() {
 5     int t, l1, l2, r1, r2, mod;
 6     cin >> t;
 7     while(t--) {
 8         cin >> l1 >> r1 >> l2 >> r2 >> mod;
 9         if(r2-l2 >= mod-1) cout << "LOSE\n";
10         else cout << "WIN\n";
11     }
12     return 0;
13 }

 

E 小Y吃苹果

题目描述

小Y买了很多苹果,但他很贪吃,过了几天一下就吃剩一只了。每一天小Y会数出自己的苹果个数X,如果X是偶数,他就会吃掉只苹果;如果X是奇数,他就会吃掉只苹果。

你知道现在苹果只剩下一只,并且小Y是在N天前买的苹果,现在小Y想知道在那天买了多少苹果。当然,可能性不止一种,你只需要求出他买的苹果数量有多少种可能。

输入描述:

输入数据只有一个整数N,表示小Y在N天前买了苹果。

输出描述:

输出一个整数,表示可能的数量种数。
示例1

输入

1

输出

2

说明

样例中小Y在一天前买了苹果,因此他只可能买了2个或者3个苹果,共2种情况。

签到题
 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <cstring>
 4 #include <vector>
 5 using namespace std;
 6 int main() {
 7     int n, ans = 0;
 8     cin >> n;
 9     cout << (1<<n) << endl;
10     return 0;
11 }

F 1 + 2 = 3?

题目描述

小Y在研究数字的时候,发现了一个神奇的等式方程,他屈指算了一下有很多正整数x满足这个等式,比如1和2,现在问题来了,他想知道从小到大第N个满足这个等式的正整数,请你用程序帮他计算一下。

(表示按位异或运算)

输入描述:

第一行是一个正整数,表示查询次数。

接着有T行,每行有一个正整数,表示小Y的查询。

输出描述:

对于每一个查询N,输出第N个满足题中等式的正整数,并换行。
示例1

输入

4
1
2
3
10

输出

1
2
4
18

要想x^(2*x) == 3*x 其实就是要让x的二进制表示没有相邻的1,这样就好算了

当只有一位时,就只有1
两位时,至于10
三位时,100、101
四位时,1000、1001、1010
用数组dp保持第i位时的数量,很容易发现dp[i] = dp[i-1] + dp[i-2];

当要算第n个数时,要让dp数组前缀和。这样dp[i]表示,当位数最多为i位时最多有多少个。
所以可得,dp[i]+1表示第i+1位的最新数位1<<i;
这样求第n个数时,只需查看dp[i]+1 <= n < dp[i+1]+1即可,然后依次减去dp[i]+1。
 1 #include <iostream>
 2 #include <stdio.h>
 3 #define ll long long
 4 using namespace std;
 5 ll dp[61], n, sum[61];
 6 int main() {
 7     dp[1] = 1;
 8     dp[2] = 1;
 9     for(int i = 3; i <= 60; i ++) {
10         dp[i] = dp[i-1]+dp[i-2];
11     }
12     for(int i = 1; i <= 60; i ++) {
13         dp[i] = dp[i] + dp[i-1];
14     }
15     //cout << dp[60] << endl;
16     int t;
17     cin >> t;
18     while(t--) {
19         ll ans = 0;
20         cin >> n;
21         while(n) {
22             for(int i = 0; i <= 60; i ++) {
23                 if((dp[i+1]+1) > n && n >= (dp[i]+1)) {
24                     ans += (1LL<<i);
25                 //  cout << ans << endl;
26                     n -= (dp[i]+1LL);
27                     break;
28                 }
29             }
30         }
31         cout << ans << endl;
32     }
33     return 0;
34 }

I 二数

题目描述

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

输入描述:

1 ≤ T ≤ 100, 1 ≤ n ≤ 10100000 − 1, T组数据的数字的十进制表示长度总和不超过1000000

输出描述:

每行一个整数 m 第 i 行表示第 i 个数所对应的“最邻近二数”
示例1

输入

5
42
11
1
2018
13751

输出

42
8
0
2020
8888

有规律,先找到第一个奇数,让它加1,然后让后面的全部变成0,或者让它加1,让后面的全部变成8,看那个数相差最小,相同的话输出最小的数。
  1 #include <iostream>
  2 #include <cstring>
  3 #include <stdio.h>
  4 #define MAX 1000010
  5 using namespace std;
  6 const int N = 1e6+10;
  7 char str[N], str1[N], str2[N];
  8 int sum1[MAX] = {0};
  9 int sum2[MAX] = {0};
 10 
 11 int Subtraction(char num1[], char num2[], int sum[])
 12 {
 13     int i, j, len, blag;
 14     char *temp;
 15     int n2[MAX] = {0};
 16     int len1 = strlen(num1);
 17     int len2 = strlen(num2); 
 18     blag = 0; 
 19     if(len1 < len2) 
 20     {
 21         blag = 1; 
 22         temp = num1;
 23         num1 = num2;
 24         num2 = temp;
 25         len = len1;
 26         len1 = len2;
 27         len2 = len;
 28     }
 29     else if(len1 ==len2) 
 30     {  
 31         for(i = 0; i < len1; i++)
 32         {
 33             if(num1[i] == num2[i])
 34                 continue;
 35             if(num1[i] > num2[i])
 36             {
 37                 blag = 0; 
 38                 break;
 39             } 
 40             else
 41             {
 42                 blag = 1; 
 43                 temp = num1;
 44                 num1 = num2;
 45                 num2 = temp;
 46                 break;
 47             } 
 48         } 
 49     }
 50     len = len1>len2 ? len1 : len2; 
 51     for (i = len1-1, j = 0; i >= 0; i--, j++) 
 52         sum[j] = num1[i] - '0';
 53 
 54     for (i = len2-1, j = 0; i >= 0; i--, j++)
 55         n2[j] = num2[i] - '0';
 56    
 57     for (i = 0; i <= len; i++)
 58     {
 59         sum[i] = sum[i] - n2[i]; 
 60         if (sum[i] < 0)   
 61         {    
 62             sum[i] += 10;
 63             sum[i+1]--;
 64         }
 65     }
 66     
 67     for (i = len1-1; i>=0 && sum[i] == 0; i--)
 68         ;
 69     len = i+1;
 70     if(blag==1)
 71     {
 72         sum[len] = -1;  
 73         len++;
 74     }
 75     return len;   
 76 }
 77 
 78 
 79 
 80 void init() {
 81     int len = strlen(str+1);
 82     //cout << len << endl;
 83     int i = 1;
 84     //cout << "dsadsa\n";
 85     while((str[i]-'0')%2==0 && i <= len) {
 86         str1[i] = str[i];
 87         str2[i] = str[i];
 88         i++;
 89     }
 90     
 91     if(str[i]=='9' && i <= len){
 92         str1[i-1] += 2;
 93         str1[i] = '0';
 94         str2[i] = str[i] - 1;
 95     } else if(i <= len){
 96         str1[i] = str[i] + 1;
 97         str2[i] = str[i] - 1;
 98     }
 99     
100     for(int j = i+1; j <= len; j ++) {
101         str1[j] = '0';
102         str2[j] = '8';
103     }
104     i = 1;
105     int j = 1;
106     int len1,len2;
107     while(str2[i]=='0' && i < len)i++;
108    // cout << str1+1 << endl;
109    // cout << str2+i << endl;
110     if(str1[0] == '2') {
111         j = 0;
112     }
113     len1 = Subtraction(str1+j, str+1, sum1);
114     len2 = Subtraction(str+1, str2+i, sum2); 
115     //cout << sum1[0] << ' ' << sum2[0] << endl;
116    // cout << len1 << ' ' << len2 << endl;
117     if(len1 < len2) {
118         cout << str1+j << endl;
119     }else if(len1 > len2) {
120         cout << str2+i << endl;
121     } else {
122         bool flag = true;
123         for(int k = len1-1; k >= 0; k --) {
124             if(sum1[k] < sum2[k]) {
125                 flag = false;
126                 break;
127             } else if(sum1[k] > sum2[k]){
128                 break;
129             }
130         }
131         if(flag) cout << str2+i << endl;
132         else cout << str1+j << endl;
133     }
134 }
135 
136 int main() {
137     int t;
138     cin >> t;
139     while(t--) {
140         memset(str,0,sizeof(str));
141         memset(str1,0,sizeof(str1));
142         memset(str2,0,sizeof(str2));
143         memset(sum1,0,sizeof(sum1));
144         memset(sum2,0,sizeof(sum2));
145         cin >> str+1;
146         init();
147     }
148     return 0;
149 }

 L K序列

题目描述

给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”。现在要你 对数组 a 求出最长的子序列的长度,满足这个序列是 K 序列。 

输入描述:

第一行为两个整数 n, K, 以空格分隔,第二行为 n 个整数,表示 a[1] ∼ a[n],1 ≤ n ≤ 105 , 1 ≤ a[i] ≤ 109 , 1 ≤ nK ≤ 107

输出描述:

输出一个整数表示最长子序列的长度 m
示例1

输入

7 5
10 3 4 2 2 9 8

输出

6

暴力即可解决。
动态规格解点击
 1 #include <iostream>
 2 #include <stdio.h>
 3 #define ll long long
 4 using namespace std;
 5 const int N = 1e5+10;
 6 ll a[N];
 7 int main() {
 8     int n, k;
 9     scanf("%d %d",&n, &k);
10     for(int i = 1; i <= n; i ++) {
11         scanf("%d",&a[i]);
12         a[i] += a[i-1];
13     }
14     int MAX = 0;
15     for(int i = 1; i <= n; i ++) {
16         for(int j = n; j >= 1; j --) {
17             if(MAX >= j-i+1) break;
18             if((a[j]-a[i-1])%k==0 && j-i+1 > MAX) {
19                 //printf("%d %d %d %d\n",i,j,a[j],a[i-1]);
20                 MAX = j-i+1;
21             }
22         }
23     }
24     cout << MAX << endl;
25     return 0;
26 }

 

posted @ 2018-04-15 17:48  starry_sky  阅读(820)  评论(6编辑  收藏  举报