1. 程式人生 > 實用技巧 >Codeforces Global Round 11 E.Xum (exgcd,構造,思維)

Codeforces Global Round 11 E.Xum (exgcd,構造,思維)

題目:傳送門

題意

給你一個序列,序列裡一開始只有一個奇數 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\n", (int)ans.size()); for(auto v : ans) { printf("%lld %c %lld\n", v.x, "+^"[v.op], v.y); } } int main() { // int _; scanf("%d", &_); // while(_--) solve(); solve(); return 0; }