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;
}
1 条评论
Linky · 2022年3月18日 12:24 下午
qwq