ARC185 A-E

ddxrS發表於2024-10-14

比 ARC184 簡單多了。

A. mod M Game 2

我們只關心 Alice 出的最後一張牌,這張牌會決定遊戲的勝負,因為除了最後一張牌,二人都可以選擇出牌來使得自己不會輸。

讓我們假設 Alice 最後出的牌為 \(x\),那麼在打出最後一張牌之前的牌之和為 \(n(n-1)-x\)

  1. \([n(n-1)-x]\bmod m=0\),令 \(y=n(n-1)\bmod m\),如果 Bob 打出的前 \(n-1\) 張牌中沒有 \(y\),那麼 \(x=y\),此時 Alice 會輸。可以被證明,Bob 一定存在一種策略能夠達到這種狀態。
  2. \([n(n-1)-x]\bmod m\not=0\),此時 Alice 必勝。

條件 1 就等價於 \(1\le n(n-1)\bmod m\le n\)

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll n, m, N;
int main() {
	int t; cin>>t;
	while(t--) {
		scanf("%lld %lld", &n, &m); N = n;
		n = n * (n + 1);
		if(n % m >= 1 && n % m <= N) puts("Bob");
		else puts("Alice");
	}
	return 0;
}

B. +1 and -1

貪心的想,我們想盡量讓最後的 \(A\) 中的元素儘量接近(平均值),令 \(s=\sum A_i\),那麼最終的序列 \(A=(\lfloor \frac{s}{n}\rfloor,\lfloor \frac{s}{n}\rfloor,\dots,\lfloor \frac{s}{n}\rfloor+1,\dots,\lfloor \frac{s}{n}\rfloor+1)\),其中 \(\lfloor \frac{s}{n}\rfloor+1\) 共有 \(s\bmod n\) 項。

之後我們就只需要判斷 \(A\) 是否可能透過操作變成上述的樣子,這是容易的。

詳細證明可以見 Atcoder 官解。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
int T, n, a[N], b[N], sum;
void solve() {
	cin>>n; sum = 0;
	for(int i = 1; i <= n; i++) cin>>a[i], sum += a[i];
	for(int i = 1; i <= n; i++) {
		b[i] = sum / n + ((n - i + 1) <= sum % n);
		if(a[i] > b[i]) return puts("NO"), void();
		int tmp = b[i] - a[i];
		a[i] += tmp, a[i + 1] -= tmp;
	}
	puts("YES");
}
signed main() {
	cin>>T;
	while(T--) solve();
	return 0;
}

C. Sum of Three Integers

題目只要求給出任意一組合法解。

讓我們列舉 \(i=1,2,\dots,n\),之後我們就只需要判斷是否有兩個整數 \(j,k\) 滿足 \(A_j+A_k=X-A_i\)

我們可以透過一次卷積來解決這個問題。

具體的,我們可以透過一次卷積求出有多少個 \(A_j+A_k(1\le j,k\le n)\) 等於某個值 \(Y\),但是可能 \(j=k\),此時將每一個 \(A_p+A_p\) 的方案減去一。

除此之外,還可能存在 \(A_i+A_k=X-A_i\) 也就是一個 \(A_i\) 出現兩次的情況,我們可以先提前預處理出滿足這種情況的方案數,最後在減去即可。

時間複雜度 \(O(X\log X)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e6 + 5;
const double Pi = acos(-1);
int n, x, m, lim = 1, l, r[MAXN];
long long p[MAXN], num[MAXN], cnt[MAXN], cnt0, maxn;
vector<int> ans;
struct Complex {
	double x, y;
	Complex() {}
	Complex(double x, double y) : x(x), y(y) {}
	Complex operator +(Complex t) {return Complex(x + t.x, y + t.y);}
	Complex operator -(Complex t) {return Complex(x - t.x, y - t.y);}
	Complex operator *(Complex t) {return Complex(x * t.x - y * t.y, x * t.y + y * t.x);}
} a[MAXN];
void fft(Complex *A, int opt) {
	for(int i = 0; i < lim; i++) if(i < r[i]) swap(A[i], A[r[i]]);
	for(int mid = 1; mid < lim; mid <<= 1) {
		Complex w = Complex(cos(Pi / mid), opt * sin(Pi / mid));	
		for(int len = mid << 1, i = 0; i < lim; i += len) {
			Complex p = Complex(1, 0);
			for(int j = 0; j < mid; j++, p = p * w) {
				Complex x = A[i + j], y = p * A[i + mid + j];
				A[i + j] = x + y, A[i + mid + j] = x - y;
			}
		}
	}
}
long long flg[MAXN], sum[MAXN];
int main() {
	scanf("%d %d", &n, &x);
	for(int i = 0; i < n; i++) {
		scanf("%d", &p[i]);
		maxn = max(maxn, p[i] * 2);
		cnt[p[i]]++;
	}
	for(int i = 0; i < n; i++) if(x >= 2 * p[i]) sum[i] = (cnt[x - 2 * p[i]] - (p[i] == (x - 2 * p[i]))) * 2;
	while(lim <= maxn) lim <<= 1, l++;
	for(int i = 0; i < lim; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
	for(int i = 0; i < lim; i++) a[i].x = cnt[i];
	fft(a, 1);
	for(int i = 0; i < lim; i++) a[i] = a[i] * a[i]; 
	fft(a, -1);
	for(int i = 0; i < lim; i++) num[i] = (a[i].x / lim + 0.5);
	for(int i = 0; i < n; i++) num[p[i] * 2]--;
	for(int i = 0; i < n; i++) {
		if(num[x - p[i]] - sum[i] >= 1) {
			flg[p[n - 1]] = n - 1;
			for(int j = n - 2; j > i; j--) {
				if(flg[x - p[i] - p[j]]) {
					printf("%d %d %d", i + 1, j + 1, flg[x - p[i] - p[j]] + 1);
					return 0;
				}
				flg[p[j]] = j;
			}
			for(int j = i + 1; j <= n - 1; j++) flg[p[j]] = 0;
		}
	}
	puts("-1");
	return 0;
} 

D. Random Walk on Tree

沒有場切。

先讓我們考慮 \(m=1\) 的情況。

此時 Takahashi 會在節點 \(0\) 和一個葉子節點之間反覆移動,顯然,Takahashi 想要給這 \(n\) 個葉子節點都染上色的期望往返次數為 \(\sum_{i=0}^{n-1}\frac{n}{n-i}\),每次往返需要 \(2\) 步,且最後一次不需要往返,所以期望步數為 \(2(\sum_{i=0}^{n-1}\frac{n}{n-i})-1\)

接下來讓我們單獨考慮一條長度為 \(m\) 的鏈的情況,轉化為這樣一個問題:初始時 \(i=0\),每次隨機將 \(i\) 變為 \(i+1\)\(i-1\),當 \(i=0\) 時只有第一種操作,求出 \(i\) 變為 \(m\) 的期望操作次數。

\(f_i\) 表示將 \(i\) 變為 \(i+1\) 的期望次數,令 \(g_i\) 表示將 \(i+1\) 變為 \(i\) 的期望次數,我們有:

  • \(i=0\) 時,有 \(f_0=g_0+1\)
  • \(i=m-1\) 時,有 \(f_{m-1}=0,g_{m-1}=1\)
  • \(1\le i\le m-1\)\(f_i+g_{i-1}=f_{i+1}+g_i\),因為中間所有點的期望進出次數是相同的。

若我們固定了移動的起點,在每一個點時它進行兩種操作的機率時相同的,所以進行操作的期望次數與它具體是那種操作無關,於是有 \(f_i=g_{i-1}\)

從大到小考慮每個點,有 \(f_i=m-i,g_i=m-i-1\),所以總期望次數為 \(m^2\)

最後的答案就是 \((2(\sum_{i=0}^{n-1}\frac{n}{n-i})-1)m^2\)

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll mod = 998244353;
ll qpow(ll a, ll b) {ll ans = 1; while(b) {if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1;} return ans;}
ll n, m, ans = 0;
int main() {
	scanf("%lld %lld", &n, &m);
	for(int i = 1; i <= n; i++) ans = (ans + n * qpow(i, mod - 2) % mod) % mod; 
	printf("%lld\n", (2 * ans + mod - 1) % mod * m % mod * m % mod);
	return 0;
}

E. Adjacent GCD

依次列舉每一個 \(m=i=1,2,\dots n\) 來求解答案,記 \(m=i\) 的答案為 \(ans_i\)

在我們不考慮 \(i\) 時,答案為 \(2ans_{i-1}\)

現在讓我們考慮 \(i\),記子序列中的上一個選擇的數為 \(j\),那麼最新造成 \(\gcd(a_i,a_j)2^{j-1}\) 的貢獻。

也就是說 \(ans_i=2ans_{i-1}+\sum_{j=1}^{i-1}\gcd(a_i,a_j)2^{j-1}\)

現在讓我們考慮後面那一坨玩意。考慮求出每個 gcd,然後再乘上 \(2^{j-1}\),但這樣算肯定不對,因為我們無法確定這個就是那兩個數的 gcd,無法保證它們沒有其它的質因子。

但是,我們知道 \(x=\sum_{d\mid x}\varphi(d)\)

也就是說,我們只需要列舉 \(a_i\) 的每個因子 \(p\),看有多少個數包含這個因子 \(p\),那麼這個因子對答案造成的貢獻就為 \(p\cdot\varphi(p)\)

時間複雜度 \(O(n\sqrt{a_i})\)

//by Larrix
#include <bits/stdc++.h>
#define int long long
using namespace std;
constexpr int Mod = 998244353;
int n, prime[100005], phi[100005], cnt, rs, f[100005], fc[500005];
bool vis[100005];
void GetPrime() {
	vis[1] = phi[1] = 1;
    fc[1] = 2, fc[0] = 1;
	for (int i = 2; i <= 100000; i = -~i) {
        fc[i] = fc[i - 1] * 2 % Mod;
		if (!vis[i]) prime[++cnt] = i, phi[i] = i - 1;
		for (int j = 1; j <= cnt && i * prime[j] <= 100000; j = -~j) {
			vis[i * prime[j]] = 1;
			if (i % prime[j] == 0) phi[i * prime[j]] = phi[i] * prime[j];
			else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
			if (i % prime[j] == 0) break;
		}
	}
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    GetPrime();
    cin >> n;
    fc[0] = 1;
    for (int i = 1, x; i <= n; i++) {
        fc[i] = fc[i - 1] * 2 % Mod;
        cin >> x;
        rs = rs * 2 % Mod;
        for (int j = 1; j * j <= x; j++) if (x % j == 0) {
            rs = (rs + f[j]) % Mod;
            f[j] = (f[j] + phi[j] * fc[i - 1]) % Mod;
            if (j * j == x) continue;
            rs = (rs + f[x / j]) % Mod;
            f[x / j] = (f[x / j] + phi[x / j] * fc[i - 1]) % Mod;
        }
        cout << rs << '\n';
    }
    return 0;
}