给定一个
N " role="presentation"> 行N " role="presentation"> 列的棋盘,第i " role="presentation"> 行只有[ 1 , h i ] " role="presentation"> 是有格子的,其他都是虚空。一个棋子放在一个格子上,我们称一个格子被一个棋子覆盖,仅当这个格子与这个棋子在同一行或同一列,且他们中间没有虚空。特别的,如果这个格子上有棋子,这个格子也被这个棋子覆盖。
求将棋盘上每个格子都覆盖的方案数,对
998244353取模。
1 ≤ h i ≤ N ≤ 400 " role="presentation">。
首先有一个最基础的暴力,直接容斥。钦定集合
观察求方案的过程发现,如果在一行钦定了一个格子不选,则整一行都不能放棋子,而且行是连续的,列是不连续的,不妨将行作为一个整体进行。
枚举选择了格子的集合
设列的长度为
- 如果在这一列没有选择格子,则都可以放置棋子,方案数
" role="presentation">;2 l e n − p - 如果在选择了格子,那么整一列不能放置,选择格子的方案数为
∑ i = 1 p ( p i ) × ( − 1 ) i = − [ p > 0 ] " role="presentation">。
上面式子中由于
但是,仅仅这样判断不能保证
设
设列有
- 这一列没有选择格子,方案数
" role="presentation">;2 l e n − p - 如果选择了格子,选择的方案数为
∑ i = 1 p − q × ( − 1 ) i = − [ p > q ] " role="presentation">。
这样只用枚举出
在同一列中,它的方案数为
计算需要减去的贡献,就相当于计算
这样依赖的关系很像 SP3734 PERIODNI - Periodni,图都是一样的,将网格剖分成笛卡尔树的形式。
设
- 这个连续块中依然保持
p = q " role="presentation">,从儿子中[ p = q ] " role="presentation"> 转移过来。 - 否则,总方案减去上面
[ p = q ] " role="presentation"> 的方案数量就是剩余方案数。
在每个连续段中方案数就等于
将连续段空行的儿子当做特殊的儿子,上面取就是
用树形 DP 完成啦!
- #define Maxn 405
- #define mod 998244353
- int n,tot,All,ans;
- int len[Maxn],h[Maxn],siz[Maxn];
- int pow2[Maxn][Maxn][2],dp[Maxn][Maxn][2],tmp[Maxn][2];
- vector<int> g[Maxn];
- inline void init()
- {
- for(int i=0,cur=1;i<=n;i++)
- {
- pow2[i][0][0]=pow2[i][0][1]=1;
- for(int j=1;j<=n;j++)
- pow2[i][j][1]=1ll*cur*pow2[i][j-1][1]%mod,
- pow2[i][j][0]=1ll*(cur-1+mod)%mod*pow2[i][j-1][0]%mod;
- cur=cur*2ll%mod;
- }
- }
- int build(int l,int r,int cur)
- {
- int minn=inf,num=++All,Last=l-1;
- for(int i=l;i<=r;i++) minn=min(minn,h[i]);
- for(int i=l;i<=r;i++)
- if(h[i]==minn)
- {
- if(Last+1<=i-1) g[num].pb(build(Last+1,i-1,minn));
- g[num].pb(0),Last=i;
- }
- if(Last
pb(build(Last+1,r,minn)); - len[num]=minn-cur;
- return num;
- }
- void solve(int u)
- {
- if(!u) return;
- dp[u][0][1]=1;
- for(int v:g[u])
- {
- solve(v);
- for(int i=0;i<=siz[u]+siz[v];i++) tmp[i][0]=tmp[i][1]=0;
- for(int su=siz[u];su>=0;su--)
- for(int sv=siz[v];sv>=0;sv--)
- {
- ll tall=1ll*(dp[u][su][0]+dp[u][su][1])*(dp[v][sv][0]+dp[v][sv][1])%mod;
- ll tsame=1ll*dp[u][su][1]*dp[v][sv][1]%mod;
- tmp[su+sv][0]=(1ll*tmp[su+sv][0]+tall-tsame+mod)%mod;
- tmp[su+sv][1]=(tmp[su+sv][1]+tsame)%mod;
- }
- for(int i=0;i<=siz[u]+siz[v];i++) dp[u][i][0]=tmp[i][0],dp[u][i][1]=tmp[i][1];
- siz[u]+=siz[v];
- }
- for(int i=0;i<=siz[u];i++)
- dp[u][i][0]=1ll*dp[u][i][0]*pow2[siz[u]-i][len[u]][0]%mod,
- dp[u][i][1]=1ll*dp[u][i][1]*pow2[siz[u]-i][len[u]][1]%mod;
- }
- int main()
- {
- n=rd(),init();
- for(int i=1;i<=n;i++) h[i]=rd();
- siz[0]=1,dp[0][1][1]=mod-1,dp[0][0][1]=dp[0][1][0]=1;
- build(1,n,0),solve(1);
- for(int i=0;i<=n;i++) ans=(1ll*ans+dp[1][i][0]+dp[1][i][1])%mod;
- printf("%d\n",ans);
- return 0;
- }
