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 ",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 ",ans); 30 tmp -= a[j]; 31 a[j] = 0; 32 } else{ 33 ans += (j-i)*tmp; 34 //printf("3:%d ",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 "; 10 else cout << "WIN "; 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位的数字,请你求出和这个数字的差的绝对值最小的二数,若答案不唯一,输出最小的那个。
换句话说,给定一个十进制下最多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 "; 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 ",i,j,a[j],a[i-1]); 20 MAX = j-i+1; 21 } 22 } 23 } 24 cout << MAX << endl; 25 return 0; 26 }