第十六屆上海大學程序設計聯賽春季賽暨上海高校金馬五校賽


A Wasserstein Distance

題目描述

最近對抗生成網絡(GAN)很火,其中有一種變體WGAN,引入了一種新的距離來提高生成圖片的質量。這個距離就是Wasserstein距離,又名鏟土距離。
這個問題可以描述如下:


有兩堆泥土,每一堆有n個位置,標號從1~n。第一堆泥土的第i個位置有a i克泥土,第二堆泥土的第i個位置有b i克泥土。小埃可以在第一堆泥土中任意移挪動泥土,具體地從第i個位置移動k克泥土到第j個位置,但是會消耗 的體力。小埃的最終目的是通過在第一堆中挪動泥土,使得第一堆泥土最終的形態和第二堆相同,也就是a i=b i (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...,他想知道從一個數開始數最少的數就得到一個二數。但是聰明的小森已經偷偷在心里算好了小埃會數到哪個二數,請你求出他要數到哪個數吧。
換句話說,給定一個十進制下最多10 5位的數字,請你求出和這個數字的差的絕對值最小的二數,若答案不唯一,輸出最小的那個。
也就是說,給定數字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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM