2019 年的尾巴在 2021 年的钟声即将敲响之际被解决了!

我的肯定不是最快的,但一定是很短的。

没看懂其他人在干什么干脆自己瞎推了。

考虑令 $pos$ 表示全局最高点(如果有多个,就选最右边的那个),容易发现 $[1,pos-1]$ 的点都无法移动到 $pos$ 后面,$[pos+1,n]$ 的点都无法移动到 $pos$ 前面,这下两边就变成了互不影响的区间。可以想到以此来区间 DP 。

令 $dp_{l,r}(x)$ 表示区间 $[l,r]$ 最大值为 $x$ 时的方案数,转移的时候枚举一下最大值可能的位置转移即可,注意用前缀和优化。

这下子的话复杂度是 $O(n^2m)$ 的,其中 $m$ 为权值最大值,不可接受。考虑到一个区间内可能转移的最大值只有 $O(1)$ 个,可能的区间数或许远没有 $n^2$ 。打表可以发现 $n\leq 300$ 时区间数是 $2518$ ,这样就可以通过 $50$ 分了。

void solve(int l,int r) {
    if(vis[l][r]) return ;
    int now=++tot; vis[l][r]=now;

    if(l>r) dp[now][0]=1;
    else {
        lep(i,l,r) if(abs(i-l-r+i)<=2) {
            solve(l,i-1),solve(i+1,r);
            lep(j,a[i],b[i]) pls(dp[now][j],mul(dp[vis[l][i-1]][j],dp[vis[i+1][r]][j-1]));
        }
    }
    lep(j,1,m) pls(dp[now][j],dp[now][j-1]);
}

接下来一档分是 $a=1,b=10^9$ 的。不妨思考一下,这档 $a,b$ 均一样的分意义何在?

首先将 $a,b$ 集体减一。可以发现当 $l=r$ 的时候,$dp_{l,r}(x)$ 多项式的最高项是 $x^{0}$ ,其前缀和的多项式最高项是 $x^{1}$ 。当两边合并的时候,继续观察,不难发现 $dp_{l,r}(x)$ 的前缀和是 $r-l+1$ 次多项式。

我们的目标是 $dp_{1,n}(m)$ ,直接拉插即可。

满分做法?容易发现比如说 $a_i=3,b_i=5$ 时,$dp_{i,i}(x)$ 的最高项并不是 $x^{0}$ ,称这种情况为” 不满足条件”。但是如果将值域段定为 $[3,5]$ ,就没问题了。而且可以发现,如果定义一段值域 $[3,4]$ 以及一段值域 $[5,5]$ ,那么 $dp_{i,i}(x)$ ,对于第一段值域满足条件,对于第二段值域仍然满足条件(显然前面值域段做前缀和后对于当且值域段都是常数项)。

回忆 APIO2016 划艇,对于每个 $[a_i,b_i]$ 将其拆分为若干个左闭右开的区间 $[c_t,c_{t+1})$ ,容易发现处理后只有 $O(n)$ 个左闭右开的区间。

这样逐段 DP 即可。每一段 DP 只需要 DP $[c_t,c_{t}+n-1]$ 这段区间,然后就可以拉插了。具体地,对每个 DP 状态的前缀和都拉插求出其在 $c_{t+1}-1$ 时的取值后继续做下一段即可。

因为拉插时下标连续,所以可以线性拉插,因此拉插一次时间复杂度 $O(n)$ 。每一轮一共有 $m\leq2518$ 个状态需要拉插,一共有 $O(n)$ 轮($O(n)$ 段左闭右开的区间),因此时间复杂度是 $O(n^2m)$ 的,实现精细可以通过。

不知道为什么 UOJ 上 T 成 95 了。

// {{{ solve

bool vis[N][N];
int L,T,tot,id[N][N],dp[S][N];

void init(int l,int r) {
    if(id[l][r]) return ; id[l][r]=++tot;
    if(l<=r) lep(i,l,r) if(abs(i-l-r+i)<=2) init(l,i-1),init(i+1,r);
}
void solve(int l,int r) {
    if(vis[l][r]) return ; vis[l][r]=true;
    if(l>r) dp[id[l][r]][0]=1;
    else {lep(i,l,r) if(abs(i-l-r+i)<=2&&(a[i]<=T&&T<b[i])) {
        solve(l,i-1),solve(i+1,r);
        lep(j,1,L) pls(dp[id[l][r]][j],mul(dp[id[l][i-1]][j],dp[id[i+1][r]][j-1]));
    }}
    lep(j,1,L) pls(dp[id[l][r]][j],dp[id[l][r]][j-1]);
}

// }}}

int tmp[N],invfac[N]; inline void lagrange(const int l,const int r) {
    if(n+l>=r) {lep(t,1,tot) dp[t][0]=dp[t][r-l+1]; return ;}
    tmp[n+1]=1; rep(i,n,1) tmp[i]=mul(tmp[i+1],r-i-l);
    lep(t,1,tot) dp[t][0]=0;

    int qwq=1; lep(i,l,l+n) {
        int res=mul(mul(tmp[i-l+1],qwq),mul(
            ((l-n+i)&1)?mod-invfac[l+n-i]:invfac[l+n-i],invfac[i-l]));
        lep(t,1,tot) pls(dp[t][0],mul(res,dp[t][i-l+1]));
        qwq=mul(qwq,r-i);
    }
}

int c[N<<1]; int main() {
    scanf("%d",&n);
    lep(i,1,n) scanf("%d%d",&a[i],&b[i]),c[++m]=a[i],c[++m]=++b[i];
    std::sort(c+1,c+1+m),m=std::unique(c+1,c+1+m)-c-1;
    lep(i,1,n)
        a[i]=std::lower_bound(c+1,c+1+m,a[i])-c,
        b[i]=std::lower_bound(c+1,c+1+m,b[i])-c;

    invfac[0]=1;
    lep(i,1,n+3) invfac[i]=mul(invfac[i-1],i);
    invfac[n+3]=modpow(invfac[n+3],mod-2);
    rep(i,n+3,1) invfac[i-1]=mul(invfac[i],i);

    init(1,n); for(T=1;T<m;++T) {
        L=min(c[T+1]-c[T],n+1),memset(vis,0,sizeof(vis));
        lep(l,1,n) lep(r,1,n) if(id[l][r]) solve(l,r);
        lagrange(c[T],c[T+1]-1);
        lep(t,1,tot) lep(i,1,L) dp[t][i]=0;
    }
    printf("%d\n",dp[vis[1][n]][0]);
    return 0;
}
分类: 文章

Qiuly

QAQ

1 条评论

Linky · 2022年3月18日 12:24 下午

qwq

回复 Linky 取消回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注