251118 模拟赛
T1
发现用 \(1,2\) 把序列划分成 \(2\) 段,每段内单调递增,随后简单 dp 即可。
T2
有一个 \(n\times m\) 的矩阵,矩阵上下左右连通。你初始在矩阵左上角 \((1,1)\) 处,每次会移动到右上、右、右下三个格子中权值最大的那个格子上。维护两种操作:
- 移动 \(k\) 步,并输出移动后的结果,本次询问会影响到之后的所有询问;
- 单点修改矩阵权值;
\(n,m\le 2000,~q\le 5000\)
只要维护出从第一列每个格子走 \(m\) 步后会走回到哪个格子上,就可以简单跳基环树了。
修改一个点时,只会直接影响到它的左下,左,左上三个格子。暴力更新三个格子的目的地,然后从右往左递推哪些区间分别可以到达这些格子。每倒推一步区间只会变化 \(O(1)\),因此可以做。
T3
咕咕咕
T4
称一个长度为 \(n\) 的正整数数列的权值为其最长上升区间的长度。
给定 \(n\) 个数 \(m_{1\sim n}\),称一个整数序列合法当且仅当 \(\forall i,~a_i\in [1,m_i]\)。求所有合法序列的权值和,答案对 \(1004535809\) 取模。
\(n\le 500,~1\le m_i\le 10^9\)
先拆贡献,问题转化成对每个 \(k\) 统计出权值不小于 \(k\) 的区间数量。考虑容斥,钦定一个下标集合满足向后 \(k\) 个元素是上升的,赋予容斥系数 \((-1)^{|S|+1}\)。这样一个下标集合对应的上升区间可能会重叠,合并成大区间,这样一个大区间是整个被钦定上升的。因此考虑对每一个上升区间计数容斥系数之和,设 \(g_i\) 表示长度为 \(i\) 的上升区间的容斥系数之和,有转移:\(g_i=-\sum_{j=1}^{k-1}g_{i-j}\)。现在问题转化为预处理每个区间钦定上升的方案数。
发现直接做是没有前途的。考虑优化朴素的想法:枚举右端点,然后从右向左加入左端点,每次 \(O(V)\) 的更新 dp 数组。这种每次做后缀和的 dp 数组整个是一个多项式,考虑维护 \(\sum_{j=0}^{n}p_jx^{\overline{j}}\),令其点值为 dp 数组的值。动态维护当前多项式值有效的区间 \(x\in [1,lim]\),做完后缀和之后多项式变成:
\[
\begin{align*}
\sum_{j=0}^{n}p_j\sum_{k=x+1}^{lim_{i+1}}k^{\overline{j}}&=\sum_{j=0}^{n}p_j\sum_{k=x+1}^{lim_{i+1}}\frac{1}{j+1}\big(k^{\overline{j+1}}-(k-1)^{\overline{j+1}}\big)\\
&=\sum_{j=0}^{n}\frac{\big(lim^{\overline{j+1}}-x^{\overline{j+1}}\big)}{j+1}p_j
\end{align*}
\]
这样可以 \(O(n)\) 移动左端点。
代码
| #include<iostream>
#define int long long
using namespace std;
const int N = 510;
const int MOD = 1004535809;
const int INF = 0x3f3f3f3f;
inline void add(int &a, int b) { a += b; (a >= MOD) && (a -= MOD); }
int n, sum, ans;
int inv[N];
int a[N];
int w[N][N], p[N];
int pw[N];
int f[N], g[N];
signed main() {
inv[1] = 1;
for(int i = 2; i < N; i++) inv[i] = MOD - inv[MOD % i] * (MOD / i) % MOD;
cin >> n; sum = 1;
for(int i = 1; i <= n; i++) cin >> a[i], sum = sum * a[i] % MOD;
for(int r = 1; r <= n; r++) {
for(int i = 0; i <= n + 5; i++) p[i] = 0;
p[0] = 1; int lim = a[r];
for(int i = r; i >= 1; i--) {
if(lim < 0) break;
pw[0] = 1;
for(int j = 1; j <= n + 2; j++) pw[j] = pw[j - 1] * (lim + j - 1) % MOD;
int s0 = 0;
for(int j = n + 2; j >= 0; j--) {
p[j + 1] = (MOD - p[j] * inv[j + 1] % MOD) % MOD;
add(s0, p[j] * inv[j + 1] % MOD * pw[j + 1] % MOD);
}
p[0] = s0;
add(w[r][i], p[0]);
lim = min(lim - 1, a[i - 1]);
}
}
add(ans, sum);
for(int k = 2; k <= n; k++) {
for(int i = 0; i <= n + 2; i++) f[i] = g[i] = 0;
g[1] = 1;
for(int i = k; i <= n; i++) {
for(int j = 1; j <= k - 1; j++) add(g[i], MOD - g[i - j]);
}
f[0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= i; j++) add(f[i], f[i - j] * w[i][i - j + 1] % MOD * g[j] % MOD);
}
add(ans, (sum - f[n] + MOD) % MOD);
}
cout << ans << '\n';
return 0;
}
|