A:智乃的gcd构造 链接
题目描述 智乃想让你构造两个正整数a,b,满足
其中|x∣表示求绝对值,gcd(a,b)表示求a和b的最大公因数。
输入描述: 1 仅一行,三个正整数x , y, z(1 ≤x , y, z≤1018 )。
输出描述: 1 2 3 请构造任意满足题目要求的a ,b ∈[1,5×1018] 。 可以证明总是存在这样的答案。
示例1
输入 复制
输出 复制
说明 1 2 3 710 −426 =284 710 +426 =1136 gcd (426 ,710 )=142
思路
思路一:就是首先进行埃式筛法选出1e7内的所有质数,然后再将a=z,然后bX这些质数一直乘到符合条件为止,这个思路思维上应该是不存在问题的,但是不够。
思路二:一步即可。
代码一(错误代码) 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 #include <iostream> #include <cmath> using namespace std;unsigned long long x, y, z, a, b;const int N = 1e7 + 10 ;unsigned long long primes[N];bool st[N];int k = 0 ;unsigned long long aa, bb;void prime () { for (int i = 2 ; i <= N; i++) { if (!st[i]) { primes[k++] = i; for (int j = i; j <= N; j += i) st[j] = true ; } } }unsigned long long abss (unsigned long long a, unsigned long long b) { if (a >= b) return a - b; else return b - a; }int main () { int k1, k2; cin >> x >> y >> z; a = ceil ((x + y) / 2.0 ); b = ceil ((y - x) / 2.0 ); prime (); for (int i = 0 ; i < k; i++) { unsigned long long kk = primes[i] * z; if (kk >= b) { bb = kk; break ; } } for (int i = 0 ; i < k; i++) { unsigned long long kk = primes[i] * z; if (kk >= a) { if (abss (kk , bb) >= x && (kk + bb) >= y) { aa = kk; break ; } } } cout << aa << " " << bb; return 0 ; }
代码二(模拟) 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 #include <iostream> #include <cmath> using namespace std;typedef unsigned long long ULL; ULL x, y, z, a, b; ULL aa, bb;int main () { int k1, k2; cin >> x >> y >> z; a = z; b = z + ((x + a) / z + 1 ) * z; if (a + b < y) { b = b + ((y - a) / z + 1 ) * z; } cout << a << " " << b; return 0 ; }#include <iostream> #include <cmath> using namespace std;typedef unsigned long long ULL;const ULL inf = 5e18 ; ULL x, y, z, a, b;int main () { cin >> x >> y >> z; cout << z << " " << inf / z * z; return 0 ; }
C:智乃的k线段区间 链接
题目描述 在数轴上有N条线段,第i条线段的左右端点分别为li,ri。
定义一段区间[L,R]包含第i条线段,当且仅当L≤li≤ri≤R。
智乃想要知道L,R∈[1,M]且L≤R时,有多少个区间[L,R]包含至少k条线段。
输入描述: 1 2 3 第一行输入三个正整数N , M , K ( 1 ≤K ≤N ≤105 , 1 ≤M ≤109 ) 。 接下来N 行,每行输入两个正整数li , ri ( 1 ≤li ≤ri ≤M ) 。
输出描述:
示例1
输入 复制
1 2 3 4 5 6 7 6 9 3 3 5 4 6 5 7 8 8 8 8 8 8
输出 复制
说明子集mex 1 2 答案为: ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,
思路
思路一: 我的基本思路就是首先将所有绳子按r来进行sort一下,然后使用滑动窗口维护一个长度为k的队列来维护l的最小值,依次 + 最小值即可(需要细节处理特殊情况,比如右端点一直为1的情况)
思路二: 上述思路实际上基本正确下面是一位佬给我提供的思路,首先枚举左端点L,把就是把li小于L的删掉,除了这些线段,其他的就是可行的,然后滑动的时候,需要删掉的左li就把对应的ri也从剩余集合删掉,然后从剩余ri中找第k小。
代码一(错解) 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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <iostream> #include <algorithm> #include <vector> using namespace std;typedef pair<int , int > PII;const int N = 1e5 + 10 ; vector<PII> query;int s[N], q[N];long long res = 0 ; vector<PII> Q;bool cmp (PII A,PII B) { return A.second < B.second || (A.second == B.second && A.first < B.first); }int main () { int n, m, k; cin >> n >> m >> k; for (int i = 1 ; i <= n; i++) { int l, r; cin >> l >> r; query.push_back ({l, r}); } sort (query.begin (),query.end (),cmp); int hh=0 ,tt=-1 ; for (int i=0 ;i<n;i++) { if (hh<=tt&&i-k+1 >q[hh]) hh++; while (hh<=tt&&query[i].first <= query[q[tt]].first) tt--; q[++tt]=i; if (i-k+1 >=0 ) Q.push_back ({query[q[hh]].first, query[i].second}); } for (int i = 0 ; i < Q.size (); i++){ if (i != Q.size () - 1 && Q[i].second != Q[i + 1 ].second) { res += Q[i].first; } else if (i == Q.size () - 1 ) { res += Q[i].first; } cout << res << '\n' ; } res += Q[Q.size () - 1 ].second * (m - query[n - 1 ].second); cout << res; return 0 ; }
代码二 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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 #include <bits/stdc++.h> using namespace std;#define int long long int x,y,z;const int N=1e5 +10 ;const int inf=1e18 ;int n,m,k;using PII = pair<int ,int >; PII p[N];int a[N],b[N];signed main () { cin>>n>>m>>k; set<int >s; for (int i=1 ;i<=n;++i) { cin>>p[i].first>>p[i].second; s.insert (p[i].first); a[i]=p[i].first,b[i]=p[i].second; } sort (p+1 ,p+1 +n); sort (a+1 ,a+1 +n); sort (b+1 ,b+1 +n); vector<int >o; o.push_back (0 ); for (auto v:s) { o.push_back (v); } int lef=0 ,rig=0 ; int ans=0 ; multiset<int >sa; multiset<int >sb; for (int i=1 ;i<=n;++i) { if (i<=k) sa.insert (b[i]); else sb.insert (b[i]); } for (int i=1 ;i<o.size ();++i) { int L=o[i]; while (lef<n&&p[lef+1 ].first<L) { lef++; int v=p[lef].second; if (v>*sa.rbegin ()) { auto it=sb.find (v); sb.erase (it); } else { if (sb.count (v)){ auto it=sb.find (v); sb.erase (it); } else { sa.erase (v); if (!sb.empty ()){ auto nxt=sb.begin (); sa.insert (*nxt); sb.erase (nxt); } else { cout<<ans<<endl; return 0 ; } } } } ans+=(m-*sa.rbegin ()+1 )*(L-o[i-1 ]); } cout<<ans<<endl; return 0 ; }
D:智乃的原始部落 链接
题目描述 有这样一个经典故事,一位探险家到某个原始部落找一位向导带路。由于原式部落没有货币,所以探险家准备使用一块长度为555的金条支付这位向导555天的工资。
双方出于对对方的不信任,想到了一个方法可以避免某一方提前跑路。探险家将金条切成长度分别为1,2,21,2,21,2,2的三部分。
第一天结束后,探险家将长度为111的金条直接支付给向导。
第二天结束后,探险家将长度为222的金条支付给向导,并向他取回长度为111的金条。
第三天结束后,探险家将长度为111的金条直接支付给向导。
第四天结束后,探险家将另一块长度为222的金条支付给向导,并向他取回长度为111的金条。
第五天结束后,探险家将长度为111的金条直接支付给向导。
这样就构建了一套货币找零系统,使探险家能够在每一天的时候,“恰好”支付向导111个单位的金条。
现在有两块金条,长度分别为N,M,他准备雇佣这位向导N+M天,假设探险家切割一次长度为N的金条需要花费a的代价,切割一次长度为M的金条需要花费b的代价。
现在智乃想要知道,探险家通过切割两块金条构建货币找零系统使得他能够每一天`恰好’支付向导1个单位的金条的最小代价的具体方案,你可以给出任意一种。
输入描述: 1 2 3 第一行输入两个正整数N,M(1 ≤N,M≤107 )表示两块金条的长度。 接下来一行输入两个整数a ,b (1 ≤a ,b ≤109 ),表示切割两块金条的代价分别是多少。
输出描述: 1 2 3 4 5 6 7 第一行输出三个整数ans,la ,lb 分别表示最小代价,第一块金条的切割次数,第二块金条的切割次数。 第二行输出一行la +1 个正整数,表示切割后第一块金条每一部分的长度。 第三行输出一行lb +1 个正整数,表示切割后第二块金条每一部分的长度。 你可以输出任意满足条件的方案,但是需要保证切割每一段的长度大于0 ,例如不能出现长度为5 的金条,切2 刀分成1 ,4 ,只能切1 刀分成1 ,4 两部分。
示例1
输入 复制
输出 复制
示例2
输入 复制
输出 复制
示例3
输入 复制
1 2 10000000 10000000 1000000000 1
输出 复制
1 2 3 23 0 23 10000000 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152 4194304 1611393
思路
链接
这个题我也没想明白为什么难,可能没看到数据范围,本来是想贪心,发现贪不了一点,打了暴力找了几天规律放弃了,然后把数据范围改成暴力能过的范围。本来是放到了C题的地方,然后被内测同学吐槽后改为了D题
当然,暴力也没有那么的暴力,还是需要一些贪心技巧的。
首先这个问题有一个IO方言,叫《子集mex》,感兴趣可以自行了解一下。
对于两块金条,假设长度分别为a,ba,ba,b,我们可以找零表示的最大可以表示的数字为s。我们可以用一个三元组(a,b,s)来表示一个当且状态。定义f(a,b,s)表示当前长度分别为a,b,最大可以表示的数字为s的最小代价。初始状态为f(la,lb,0)。
我们思考这样一个问题,当满足如下约束条件时
$ \left{\begin{matrix}a’\geq a\b’ \geq b \s’ \leq s \end{matrix}\right. $
必定存在f(a′,b′,s′)≤f(a,b,s)成立。所以每次dfs的时候贪心的取s准没错,除非金条剩余部分已经满足条件,需要直接加到sss中。
思考时间复杂度,发现最坏情况就是按照1,2,4,8,16,…倍增,所以最多递归log(max(n,m))次,每次dfs要么切要么切b,复杂度为O(2log(max(n,m)))=O(max(n,m),所以暴力即可通过本题。
代码 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 67 68 69 70 71 72 #include <bits/stdc++.h> using i64 = long long ; using namespace std;const int MAXN = 1005 ;const i64 INF = 1LL << 60 ; i64 dp[MAXN][MAXN], c[MAXN][2 ], v[MAXN]; bool vis[MAXN][MAXN]; i64 dp2[MAXN], dp3[MAXN];char s[MAXN];i64 val (int l, int r) { if (l > r) return 0 ; if (((r - l) & 1 ) == 0 ) return -INF; if (vis[l][r]) return dp[l][r]; vis[l][r] = true ; dp[l][r] = val (l + 1 , r - 1 ) + c[l][0 ] + c[r][1 ] + (v[l] * v[r]); for (int i = l + 1 ; i < r; i += 2 ) { dp[l][r] = max (dp[l][r], val (l, i) + val (i + 1 , r)); } return dp[l][r]; }int main () { int n; scanf ("%d" ,&n); scanf ("%s" ,s+1 ); for (int i = 1 ; i <= n; ++i) { scanf ("%lld" ,&v[i]); } for (int i = 1 ; i <= n; ++i) { int op = s[i] == '(' ; scanf ("%lld" ,&c[i][op]); c[i][op] *= -1 ; } for (int i = 1 ; i <= n; ++i) { dp2[i] = dp3[i] = -INF; } for (int i = 1 ; i <= n; ++i) { dp2[i] = max (dp2[i], dp2[i - 1 ] + c[i][1 ]); for (int j = i - 1 ; j >= 1 ; j -= 2 ) { dp2[i] = max (dp2[i], dp2[j - 1 ] + val (j, i)); } } for (int i = n; i; --i) { dp3[i] = max (dp3[i], dp3[i + 1 ] + c[i][0 ]); for (int j = i + 1 ; j <= n; j += 2 ) { dp3[i] = max (dp3[i], dp3[j + 1 ] + val (i, j)); } } i64 ans = -INF; for (int i = 1 ; i <= n + 1 ; ++i) { ans = max (ans, dp2[i - 1 ] + dp3[i]); } printf ("%lld\n" ,ans); return 0 ; }