专栏文章

2025_7_3 T4

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miou6kq4
此快照首次捕获于
2025/12/03 01:12
3 个月前
此快照最后确认于
2025/12/03 01:12
3 个月前
查看原文
今天(2025_7_19)的题,T3吉司机线段树,T4凸优化,都是我们不大擅长的点,所以回来复习模拟赛难题
算是较简单的 NOIP T4,不过这场考试虽然质量较高,但全都是DP?不管了
首先,我们不太好根据题目看出什么性质,也不太好处理题目的限制条件,考虑容斥,假设至少经过了 ii 个不合法的点到达 (n+1,n+1)(n+1,n+1) 的方案数为 fif_i,答案为 i=0nfi×(1)i×(ni)!\sum_{i=0}^{n}f_i\times (-1)^i \times (n-i)!
麻烦的是,题目中的排列有部分数是 1-1,换句话而言,对于路径的限制是不全的,想要处理 fif_i 难上加难,但因为限制是一个排列,每行每列只会有一个不可经过的点,而且路径只能向下或右走,所以边在网格上走路径,边尝试加障碍
dpi,j,k,p,qdp_{i,j,k,p,q},指目前走到了网格 (i,j)(i,j),已经确定 kk 个障碍点,当前行有没有钦定过障碍,当前列有没有钦定过障碍,显然, dpi,j,k,p,qdp_{i,j,k,p,q} 的转移如下
dpi+1,j,k,0,q+=dpi,j,k,p,qdpi,j+1,k,p,0+=dpi,j,k,p,qdp_{i+1,j,k,0,q}+=dp_{i,j,k,p,q}\\ dp_{i,j+1,k,p,0}+=dp_{i,j,k,p,q}
这是不走障碍的情况,如果下一个点是障碍,或者下一个点满足加障碍的条件,转移如下
dpi+1,j,k+(pi+1!=j),1,1=dpi,j,k,p,qdpi,j+1,k+(pi!=j+1),1,1=dpi,j,k,p,qdp_{i+1,j,k+(p_{i+1}!=j),1,1}-=dp_{i,j,k,p,q}\\ dp_{i,j+1,k+(p_i!=j+1),1,1}-=dp_{i,j,k,p,q}
因为容斥写在了转移里,所以是减法
答案即为
i=cntndpn+1,n+1,i,0,0×(ni)!\sum_{i=cnt}^n{dp_{n+1,n+1,i,0,0}\times (n-i)!}
其中,cntcnt 是题目已给定的障碍数
初值 dp0,0,cnt,0,0=1dp_{0,0,cnt,0,0}=1
CPP
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e2+10,Mod=998244353;
int n,Q,ans;
int a[maxn],dp[maxn][maxn][maxn][2][2],mul[maxn];
bool vis[maxn];
void add(int &x,int y)
{
	x+=y;
	if(x>=Mod) x-=Mod;
}
int read()
{
	int ret=0,w=1;char ch=0;
	while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+(ch^48);ch=getchar();}
	return ret*w;
}
void pre_all()
{
	mul[0]=1;
	for(int i=1;i<maxn;i++) mul[i]=1ll*mul[i-1]*i%Mod;
}
void inpu()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		if(a[i]!=-1) vis[a[i]]=1,Q++;
	}
}
void DP()
{
	dp[0][0][Q][0][0]=1;
	for(int i=0;i<=n+1;i++) for(int j=0;j<=n+1;j++) for(int k=Q;k<=n;k++)
		for(int p=0;p<=1;p++) for(int q=0;q<=1;q++) if(dp[i][j][k][p][q])
		{
			if(i<=n)
			{
				add(dp[i+1][j][k][0][q],dp[i][j][k][p][q]);
				if(!q&&1<=j&&j<=n&&i<n&&((a[i+1]==-1&&!vis[j])||a[i+1]==j))
					add(dp[i+1][j][k+(a[i+1]!=j)][1][1],Mod-dp[i][j][k][p][q]);
			}
			if(j<=n)
			{
				add(dp[i][j+1][k][p][0],dp[i][j][k][p][q]);
				if(!p&&1<=i&&i<=n&&j<n&&((a[i]==-1&&!vis[j+1])||a[i]==j+1))
					add(dp[i][j+1][k+(a[i]!=j+1)][1][1],Mod-dp[i][j][k][p][q]);
			}
		}
}
void cal() {for(int i=Q;i<=n;i++) add(ans,1ll*dp[n+1][n+1][i][0][0]*mul[n-i]%Mod);}
void solve()
{
	inpu();
	DP();
	cal();
	printf("%d",ans);
}
int main()
{
	pre_all();
	int t=1;
	while(t--) solve();
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...