题解:P11626 [迷宫寻路 Round 3] 七连击
节选自:DP做题记录(三)(2025.4.5 - 2025.4.19)
对于一段区间的划分,很容易想到 DP。
我们设 \(f_{i, j}\) 表示把前 \(i\) 个数划分成 \(j\) 段的答案,再设 \(g_{i, j}\) 表示把前 \(i\) 个数划分成 \(j\) 段的方案数。考虑前一个划分点在什么位置,可以得到:
\[g_{i, j} = \displaystyle\sum_{k = 1}^{i - 1} g_{k, j - 1}, g_{0, 0} = 1
\]
我们再考虑划分出的这一段对答案的贡献。如果们固定了一段区间 \([l, i]\),那么这段本身的贡献就是 \(\gcd(a_l, \dots, a_i)\),而 \([1, l - 1]\) 就可以随便划分了,此时:
\[f_{i, j} = \displaystyle\sum_{k = 1}^{i - 1} f_{k, j - 1} + g_{k, j - 1} \times \gcd_{l = k + 1}^i a_l
\]
观察可以发现,\(g_{i, j}\) 的转移为上一行一段前缀的和,可以前缀和优化。而 \(f_{i, j}\) 则因为加入了 \(\gcd\) 的操作,不是很好前缀和优化。不过我们考虑当固定了右端点 \(i\),左端点 \(l\) 不断向左移动的过程中,区间 \(\gcd\) 一定是单调不升的,而且每次减小,至少除以了一个 \(2\),那么一定只用 \(O(\log n)\) 次就除到了 \(1\),然后就不会变了。于是我们二分答案找到每次区间 \(\gcd\) 变化的位置,那么左端点从上一个二分出的点 \(x\) 到这一次二分出的点 \(y\) 时,区间 \(\gcd\) 是不变的,那么此时就可以前缀和优化了。设分了 \(k\) 段,复杂度为 \(O(kn \log^2 n)\),足以通过此题。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 9, LOGN = 19, MOD = 998244353;
int st[N][LOGN], LOG2[N], a[N], g[N][9], f[N][9], sumg[N][9], sumf[N][9], n;
void build(){
LOG2[1] = 0;
for(int i = 2; i <= n; i++)
LOG2[i] = LOG2[i >> 1] + 1;
for(int i = 1; i <= n; i++)
st[i][0] = a[i];
for(int j = 1; j <= LOG2[n]; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
st[i][j] = __gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
int ask(int l, int r){
int k = LOG2[r - l + 1];
return __gcd(st[l][k], st[r - (1 << k) + 1][k]);
}
int find(int l, int r){
int val = ask(l, r), res = l, tmp = r;
while(l <= r){
int mid = (l + r) >> 1;
if(ask(mid, tmp) == val){
l = mid + 1;
res = mid;
} else
r = mid - 1;
}
return res;
}
signed main(){
scanf("%lld", &n);
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
build();
g[0][0] = sumg[0][0] = 1;
for(int i = 1; i <= n; i++){
g[i][0] = 1, sumg[i][0] = 1;
for(int j = 1; j <= 7; j++){
g[i][j] = sumg[i - 1][j - 1];
sumg[i][j] = (sumg[i - 1][j] + g[i][j]) % MOD;
}
}
for(int i = 1; i <= n; i++){
f[i][1] = ask(1, i);
sumf[i][1] = (sumf[i - 1][1] + f[i][1]) % MOD;
for(int l = 1, r; l <= i; l = r + 1){
r = find(l, i);
for(int j = 2; j <= 7; j++){
if(!f[i][j]) f[i][j] = sumf[i - 1][j - 1];//wa *2
f[i][j] = (f[i][j] + ask(l, i) * (sumg[r - 1][j - 1] - sumg[l - 2][j - 1] + MOD) % MOD) % MOD;
sumf[i][j] = (sumf[i - 1][j] + f[i][j]) % MOD;
}
}
}
printf("%lld", sumf[n][7]);
return 0;
}
本文来自博客园,作者:Orange_new,转载请注明原文链接:https://www.cnblogs.com/JPGOJCZX/p/18830484

浙公网安备 33010602011771号