[NOI2019] 机器人

听说和今年联合省选 \(T2\) 蛮像。

前置题目:
[APIO2016]划艇

先考虑暴力 \(dp\)
\(f_{i,j}\) 为前 \(i\) 所学校中,第 \(i\) 所学校参赛,并派出 \(j\) 艘划艇的方案数,这样答案即 \(\sum\sum f_{i,j}\)
转移为:
\(f_{0,1} = 1\)
\(f_{i,j} = \sum_{k = 1}^{j - 1}\sum_{p = 0}^{i - 1}f_{p,k}\)
考虑第二维大小很大,我们对其离散化:
这样变成了 \(O(n)\) 个区间,我们考虑对这些区间操作:
\(f_{i,j}\) 表示最后选取的学校的个数所落在\(j\)区间的个数
那么有:\(f_{i,j} = \sum_{k = 1}^{j - 1}\sum_{p = 0}^{i - 1} \binom{L + m - 1}{m} f_{p,k}\)
最后对后者中的前缀求和即可。

那么我们对应来这个题:
考虑如果我们使用区间\(dp\),每次选取最右边的最大值,则其成功将其划分了子问题,因为左侧无法通过来到右侧,右侧也无法来到左侧。

而且考虑到我们这个最大值,显然其可以做到左右两边,所以其在区间中可以选的位置 \(O(1)\)

打个表发现实际上只有 \(2000\) 个左右的区间会被用到。

\(f_{[l,r],j}\)\([l,r]\) 上,最大值为 \(j\) 的方案数。

那么我们可以暴力 \(dp\) 一下,大概是 \(O(n^2m)\).

点击查看代码
inline void solve(int l,int r){
	if(vis[l][r])return;
	int now = ++ cnt;vis[l][r] = now;
	if(l > r)dp[now][0] = 1;
	else{
		for(int i = l;i <= r;++i){
			if(abs(i - l - r + i) <= 2){
				solve(l,i - 1),solve(i + 1,r);
				for(int j = a[i];j <= b[i];++j)
				dp[now][j] = (dp[now][j] + dp[vis[l][i - 1]][j] * dp[vis[i + 1][r]][j - 1] % mod) % mod;
			}
		}
	}
	for(int i = 1;i <= m;++i)
	dp[now][i] = (dp[now][i] + dp[now][i - 1]) % mod; 
}

接下来,我们大胆猜想:
在一个固定值域上 \([x,y]\)
\(f_{[l,r],..}\) 是一个关于 \(k\)\(r - l + 1\) 项多项式。

证明过程略去,可以参照其他博客。

那么考虑因为每个点的值域不同,我们采用前置题目的做法:

将其分为 \(O(n)\)\([c_t,c_{t+1})\),对每一段都操作

具体操作是:对每一段都暴力\(dp\)求出前 \(\min(n,len)\) 个点值,然后插值出 \(f_{[l,r],c_{t + 1} - 1}\),作为操作下一段的常数操作,感觉看代码可能更清晰一些。

前置知识:下列代码用到了给定 \(g_{1,...k}\) 点值时的线性插值:

\(g_n = \sum_{i = 1}^k (-1) ^ {k - i} y_i \frac{pre_{i - 1}\ \ \ suf_{i + 1}}{(i-1)!(k - i)!}\),
其中 \(pre_i = \prod_{j = 1}^i (n - j)\),
\(suf = \prod_{j = i}^k(n - j)\)

复杂度大概:\(O(n^2tot)\)

点击查看代码
// #include <bits/stdc++.h>
// using namespace std;
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <ctime>
using namespace std;
typedef long long ll;
const int N = 305, S = 2222, MOD = 1e9 + 7;
int n, a[N], b[N], num[N << 1], fac[N], ifac[N], id[N][N], cnt, len, f[S][N], t, tmp[N];
struct segment { int l, r; } seg[S];
bool vis[S];
int Qpow(int a, int p)
{
    int res = 1;
    while(p)
    {
        if(p & 1) res = (ll)res * a % MOD;
        a = (ll)a * a % MOD; p >>= 1;
    }
    return res;
}
void Get(int l, int r)
{
    if(l > r || id[l][r]) return;
    id[l][r] = ++cnt; seg[cnt] = (segment){l, r};
    int mid = l + r >> 1;
    for(int i = max(l, mid - 2); i <= min(r, mid + 2); i++) if(abs(i + i - l - r) <= 2)
        Get(l, i - 1), Get(i + 1, r);
}
void Solve(int l, int r)
{
    if(l > r || vis[id[l][r]]) return;
    vis[id[l][r]] = true;
    int mid = l + r >> 1;
    for(int i = max(l, mid - 2); i <= min(r, mid + 2); i++) if(abs(i + i - l - r) <= 2 && a[i] <= t && t < b[i])
    {
        Solve(l, i - 1); Solve(i + 1, r);
        for(int j = 1; j <= len; j++)
            f[id[l][r]][j] = (f[id[l][r]][j] + (ll)f[id[l][i - 1]][j] * f[id[i + 1][r]][j - 1]) % MOD;
    }
    for(int j = 1; j <= len; j++) if((f[id[l][r]][j] += f[id[l][r]][j - 1]) >= MOD) f[id[l][r]][j] -= MOD;
}
void Lagrange(int l, int r)
{
    if(r - l <= n)
    {
        for(int i = 1; i <= cnt; i++) f[i][0] = f[i][r - l + 1];
        return;
    }
    tmp[n + 1] = 1;
    for(int i = n; i; i--) tmp[i] = (ll)tmp[i + 1] * (r - l - i) % MOD;
    for(int i = 1; i <= cnt; i++) f[i][0] = 0;
    ll prod = 1;
    for(int i = l; i <= l + n; i++)
    {
        ll res = prod * tmp[i - l + 1] % MOD * ifac[i - l] % MOD * 
                  ((l - n + i) & 1 ? MOD - ifac[l + n - i] : ifac[l + n - i]) % MOD;
        for(int j = 1; j <= cnt; j++) f[j][0] = (f[j][0] + res * f[j][i - l + 1]) % MOD;
        prod = prod * (r - i) % MOD; 
    }
}
int main()
{
    // freopen("robot.in", "r", stdin);
    // freopen("robot.out", "w", stdout);
    scanf("%d", &n); Get(1, n); // fprintf(stderr, "cnt = %d\n", cnt);
    for(int i = 1; i <= n; i++)
        scanf("%d %d", &a[i], &b[i]), num[++num[0]] = a[i], num[++num[0]] = ++b[i];
    sort(num + 1, num + num[0] + 1);
    num[0] = unique(num + 1, num + num[0] + 1) - num - 1;
    for(int i = 1; i <= n; i++)
        a[i] = lower_bound(num + 1, num + num[0] + 1, a[i]) - num, 
        b[i] = lower_bound(num + 1, num + num[0] + 1, b[i]) - num;
    fac[0] = 1;
    for(int i = 1; i < N; i++) fac[i] = (ll)fac[i - 1] * i % MOD;
    ifac[N - 1] = Qpow(fac[N - 1], MOD - 2);
    for(int i = N - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;
    fill(f[0], f[0] + N, 1);
    for(t = 1; t < num[0]; t++)
    {
        len = min(n + 1, num[t + 1] - num[t]);
        memset(vis, false, sizeof(vis));
        for(int i = 1; i <= cnt; i++) Solve(seg[i].l, seg[i].r);
        Lagrange(num[t], num[t + 1] - 1);
        for(int i = 1; i <= cnt; i++) memset(f[i] + 1, 0, len * sizeof(int));
    }
    printf("%d\n", f[1][0]);
    fprintf(stderr, "%.8lf\n", 1.0 * clock() / CLOCKS_PER_SEC);
    return 0;
}
posted @ 2022-04-16 22:10  fhq_treap  阅读(69)  评论(0编辑  收藏  举报