Description
構造一個有向圖,\(i\to j\) 的邊存在,當且僅當 \(i<j\) 且 \(\text{gcd}(i,j)>1\),求一個反鏈 \(S\),使得 \(\sum\limits_{i\in S}A_i\) 最大。
反鏈指的是一個點集,任意兩點都不能到達。
Solution
考慮 \(i\) 可以到達 \(j\) 需要滿足什麼條件,下面我們欽定 \(i<j\),設 \(f(i)\) 表示 \(i\) 的最小質因子,
- \(1\) 不能到達任何點,也不能被任何點到達。
- 當 \(i\) 和 \(j\) 都是偶數時,顯然存在 \(i\to j\)。
- 當 \(i\) 是奇數,\(j\) 是偶數時,\(i\to j\) 存在當且僅當 \(i+f(i)\le j\)。
- 當 \(i\) 是偶數,\(j\) 是奇數時,\(i\to j\) 存在當且僅當 \(i\le j-f(j)\)。
- 當 \(i\) 和 \(j\) 都是奇數時,\(i\to j\) 存在當且僅當 \(i+f(i)\le j-f(j)\)。
如何理解以上式子呢,
可以將 \(i+f(i)\) 看作 \(i\) 能到達的 編號最小的點,將 \(j-f(j)\) 看作 能到達 \(j\) 的 編號最大的點,
\(i\) 和 \(j\) 都是奇數,那加上或減去 \(f\) 就應當是偶數,我們知道偶數是可以到達的,但僅限於小的編號到大的編號,所以 \(i\to j\) 存在當且僅當 \(i+f(i)\le j-f(j)\)。
於是我們可以把 \(i\) 的影響範圍看作一個區間 \([i-f(i)+1,f+f(i)-1]\),這個區間內所有點都是和 \(i\) 沒邊的,偶數 \(i\) 的影響區間是 \([i,i]\),
那麼我們對於每個點,將它的影響區間都加上它的權值 \(a_i\)。我們知道反鏈之間是沒邊的,所以反鏈中所有點的影響區間都會有交集,因此累加權值的時候就剛好計算了答案,取最大的一個即可。
對於區間加,可以用差分維護,\(dif[i-f(i)+1]+a[i]\to dif[i-f(i)+1]\),\(dif[i+f(i)]-a[i]\to dif[i+f(i)]\)。
Code
const int N = 1e6 + 5;
int n;
ll a[N], dif[N];
int f[N], prime[N], cnt;
void Solve(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
for(int i = 2; i <= n; i++){
if(!f[i]) f[i] = prime[++cnt] = i;
for(int j = 1; j <= cnt && prime[j] <= n / i; j++){
f[prime[j] * i] = prime[j];
if(i % prime[j] == 0) break;
}
}
for(int i = 2; i <= n; i++){
if(i & 1) dif[i - f[i] + 1] += a[i], dif[i + f[i]] -= a[i];
else dif[i] += a[i], dif[i + 1] -= a[i];
}
dif[1] += a[1], dif[n + 1] -= a[1];
ll ans = 0;
for(int i = 1; i <= n; i++){
dif[i] += dif[i - 1];
ans = max(ans, dif[i]);
}
cout << ans << endl;
}