WrongAnswer_90

一言(ヒトコト)

P5469 [NOI2019] 机器人

My Blogs

P5469 [NOI2019] 机器人

首先考虑如何比较简单的进行判定合法。每一次执行一个找最大值的过程(如果有多个最大值则取最右侧的),然后从这个位置把序列劈成两半,递归判定。

因为最大值的位置能走到序列的最左侧和最右侧,所以要求最大值的位置 \(|((n-p)-(p-1))|\leq 2\)

所以考虑区间 DP,设 \(f_{i,j,k}\) 表示区间 \([i,j]\) 中最大值为 \(k\) 的方案数。转移比较简单:

\[f_{i,j,k}=\sum_{p\;\text{is}\;\text{valid}}\sum_{x\leq k}f_{i,p-1,x}\sum_{y<k}f_{p+1,j,y} \]

可以前缀和优化到 \(\mathcal O(n^2)\)。同时由于 \(p\) 特殊的位置限制,所以可以把所有会访问到的区间搜出来,发现最多 \(2000+\) 个区间,这样就有了 \(50\) 分。

考虑进行优化。先观察上面方程的初值:

\[\begin{aligned} f_{i,i+1,x}&=1\\ f_{i,i,x}&=\max(0,\min(x,r_i)-l_i+1) \end{aligned} \]

如果把 \(\max\)\(\min\) 拆开,会得到分段的一次或零次关于 \(x\) 的函数。所以每一个 \(f_{i,j}\) 都是一个关于 \(k\) 的不超过 \(n\) 次的多项式(因为最多就是 \(n\) 个一次函数相乘)。

拆开 \(\max\)\(\min\) 之后有若干个值域的连续段。在连续段 \(k\in[L,R]\) 内部,\(f_{i,j}\) 关于 \(k\) 的多项式相同。所以对于每个 \(f_{i,j}\) 都会得到 \(\mathcal O(n)\) 个分段函数。

上面的暴力 DP 瓶颈在于 \(k\) 可能非常大,但是我们知道在同一连续段下它次数是 \(\mathcal O(n)\) 的。而对于两个相邻的连续段 \([L,x]\)\([x+1,R]\),后一个连续段只会用到前一个连续段 \(k=x\) 处的 DP 值。(算 \(k=x+1\) 的时候会用到)

所以对于每个连续段 \([L,R]\),可以先暴力算出前 \(n+1\) 个点值(这在算 \(k=L\) 时会用到 \(L-1\) 处的点值),然后用拉格朗日插值求出其在 \(x\) 处的点值。这样下一个区间需要用的信息就处理好了,可以继续处理下一段。

所以思路就比较明朗了:从小到大枚举连续段,然后暴力算出所有 \(f_{i,j}\) 的前 \(n+1\) 个点值,然后用拉插算出连续段最后一个数的点值,然后去算下一个连续段。DP 的总复杂度是 \(\mathcal O(mn^2)\),其中 \(m\) 为状态数,\(2500\) 左右。

对于拉插,观察公式:

\[F(x_0)=\sum_{i=1}^{n+1}y_i\prod_{j\not=i}\frac{x_0-x_j}{x_i-x_j} \]

可以先 \(\mathcal O(n\log n)\) 或者 \(\mathcal O(n)\) 预处理后面的 \(\text{prod}\),这样对于每个状态算的时候就是 \(\mathcal O(n)\),所以这部分的总复杂度也是 \(\mathcal O(mn^2)\)。注意卡常。

一个小细节是可以把求出来 \(R\) 的点值放到 DP 数组 \(k=0\) 的位置,方便下一次暴力 DP 使用。代码目前(2024/4/17)是 优势非常小的 最优解。

int cnt=1,n,len,inv2[610],inv[610],v[310],a[310],b[310],f[2710][320],g[2710],L[2710],R[2710],id[310][310],numa[610];
queue<pii> q;
vector<pii> ve;
void dfs(int l,int r)
{
	if(l>r)return;
	if(l==r)return id[l][r]=l,void();
	if(id[l][r])return;
	id[l][r]=++cnt,ve.eb(mp(l,r));
	for(int i=l;i<=r;++i)if(abs(2*i-l-r)<=2)dfs(l,i-1),dfs(i+1,r);
}
inline bool cmp(pii p1,pii p2){return p1.se-p1.fi>p2.se-p2.fi;}
inline void calc(int l,int r)
{
	for(int i=1;i<=n;++i)
	{
		for(int j=l;j<=r;++j)
		f[i][j-l+1]=min(b[i]-a[i]+1,max(0,j-a[i]+1));
	}
	for(int i=cnt;i>n;--i)
	{
		for(int j=1;j<=r-l+1;++j)f[i][j]=0;
		if((R[i]-L[i]+1)&1)
		{
			int mid=(L[i]+R[i]-2)/2;
			for(int j=max(1,a[mid]-l+1);j<=min(b[mid]-l+1,r-l+1);++j)
			Madd(f[i][j],Cmul(f[id[L[i]][mid-1]][j],f[id[mid+1][R[i]]][j-1]));
			++mid;
			for(int j=max(1,a[mid]-l+1);j<=min(b[mid]-l+1,r-l+1);++j)
			Madd(f[i][j],Cmul(f[id[L[i]][mid-1]][j],f[id[mid+1][R[i]]][j-1]));
			++mid;
			for(int j=max(1,a[mid]-l+1);j<=min(b[mid]-l+1,r-l+1);++j)
			Madd(f[i][j],Cmul(f[id[L[i]][mid-1]][j],f[id[mid+1][R[i]]][j-1]));
		}
		else
		{
			int mid=(L[i]+R[i]-1)/2;
			for(int j=max(1,a[mid]-l+1);j<=min(b[mid]-l+1,r-l+1);++j)
			Madd(f[i][j],Cmul(f[id[L[i]][mid-1]][j],f[id[mid+1][R[i]]][j-1]));
			++mid;
			for(int j=max(1,a[mid]-l+1);j<=min(b[mid]-l+1,r-l+1);++j)
			Madd(f[i][j],Cmul(f[id[L[i]][mid-1]][j],f[id[mid+1][R[i]]][j-1]));
		}
		for(int j=1;j<=r-l+1;++j)Madd(f[i][j],f[i][j-1]);
	}
	for(int i=1;i<=cnt;++i)f[i][0]=f[i][r-l+1];
}
inline void mian()
{
	read(n),dfs(1,n),sort(ve.begin(),ve.end(),cmp),cnt=n,inv[0]=1;
	for(int i=1;i<=600;++i)inv[i]=Cmul(inv[i-1],i);
	inv[600]=power(inv[600],MOD-2);
	for(int i=599;i>=1;--i)inv[i]=Cmul(inv[i+1],i+1);
	for(auto p:ve)id[p.fi][p.se]=++cnt,L[cnt]=p.fi,R[cnt]=p.se;
	for(int i=0;i<=310;++i)f[0][i]=1;
	memcpy(inv2,inv,sizeof(inv));
	for(int i=1;i<=600;++i)if(i&1)inv2[i]=Cdel(0,inv2[i]);
	for(int i=1;i<=n;++i)read(a[i],b[i]),numa[++len]=a[i],numa[++len]=b[i]+1;
	sort(numa+1,numa+1+len),len=unique(numa+1,numa+1+len)-numa-1;
	int lim=n+1;
	for(int k=1;k<len;++k)
	{
		if(numa[k+1]-numa[k]<=lim){calc(numa[k],numa[k+1]-1);continue;}
		calc(numa[k],numa[k]+lim-1);
		for(int i=1;i<=n;++i)f[i][0]=min(b[i]-a[i]+1,max(0,numa[k+1]-a[i]));
		int all=1;
		for(int j=1;j<=lim;++j)Mmul(all,numa[k+1]-numa[k]-j);
		for(int j=1;j<=lim;++j)v[j]=Cmul(all,inv2[n+1-j],inv[j-1],power(numa[k+1]-numa[k]-j,MOD-2));
		for(int i=n+1;i<=cnt;++i)
		{
			int s=0;
			for(int j=1;j<=lim;++j)
			Madd(s,Cmul(v[j],f[i][j]));
			f[i][0]=s;
		}
	}
	write(f[n+1][0]);
}
posted @ 2024-04-17 17:52  WrongAnswer_90  阅读(16)  评论(0编辑  收藏  举报