题目:传送门
题意
给你一个序列,序列里一开始只有一个奇数 x,你可以对这个序列进行两种操作:
1.选择两个序列里的数 x, y(两个数可以相等,且只要序列里出现过就可以选择两次),向序列里面加入 x + y
2.选择两个序列里的数 x, y(两个数可以相等,且只要序列里出现过就可以选择两次),向序列里面加入 x ^ y
问怎样操作后,序列里出现1.
思路
官方题解
对于输入的 x,我们可以构造一个 y,使得 gcd(x,y) = 1;然后我们可以通过扩展欧几里得,求 ax - by = 1 的一组解,这里 a,b >= 0 且 b 是偶数,那么 ax = by + 1; 则 ax ^ by = 1,就成功构造出了 1;
#include <bits/stdc++.h> #define LL long long #define ULL unsigned long long #define UI unsigned int #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF 0x3f3f3f3f #define inf LLONG_MAX #define PI acos(-1) #define fir first #define sec second #define lb(x) ((x) & (-(x))) #define dbg(x) cout<<#x<<" = "<<x<<endl; using namespace std; const int N = 1e6 + 5; LL x; struct note { LL x, op, y; }; vector < note > ans; void exgcd(LL a, LL b, LL &x, LL &y) { if(!b) { x = 1; y = 0; return ; } exgcd(b, a % b, y, x); y -= a/b*x; } void ks_add(LL a, LL b) { LL res = -1; while(b) { if(b & 1) { if(res == -1) res = a; else { ans.pb({res, 0, a}); res += a; } } ans.pb({a, 0, a}); a += a; b >>= 1; } } void solve() { scanf("%lld", &x); LL now = 1; while(2LL * now < x) { ans.pb({now * x, 0, now * x}); now *= 2LL; } ans.pb({x, 1, now * x}); LL y = (x ^ (now * x)); LL a, b; exgcd(x, y, a, b); /// 这里求得 ax + by = 1 的一组解 a, b,由于 x,y > 1,则 a, b必定一正一负 /// 由于我们要求的是 ax - by = 1 的解,故让 b *= -1; 然后判 a 是否大于 0 b = -b; if(a <= 0) { LL c = (-a) / y + 1; a += c * y; b += c * x; } /// b 是偶数,能保证 by 是一个偶数,这样 ax ^ by 才会等于 1 if(b & 1) a += y, b += x; ks_add(x, a); ks_add(y, b); ans.pb({a * x, 1, b * y}); printf("%d ", (int)ans.size()); for(auto v : ans) { printf("%lld %c %lld ", v.x, "+^"[v.op], v.y); } } int main() { // int _; scanf("%d", &_); // while(_--) solve(); solve(); return 0; }