题解:P10600 BZOJ4350 括号序列再战猪猪侠

· · 题解

根据题目要求,容易想到区间计数动态规划。

我们只考虑左括号,设 dp_{l,r} 表示当前考虑从第 l 到第 r 个左括号时的方案数,注意,这里的状态同时包含了这些左括号对应的右括号。

考虑转移,我们设 A 为一个合法括号串,它表示的左括号区间是 [l,r],可以很自然的分三种情况:

对于这些限制的快速判断我们可以维护一个矩阵,每条信息相当于矩阵上的一个点,这样对于前文的要求可以快速用二位前缀和的值的存在与否判断是否全部满足。

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
    int w=1,s=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch)){s=s*10+(ch-'0');ch=getchar();}
    return w*s;
}
const int mod=998244353;    
const int maxn=500+10;
const int inf=1e13+7;
int n,m;
int dp[maxn][maxn],sum[maxn][maxn],a[maxn][maxn];
int ask(int l1,int r1,int l2,int r2)
{return sum[l2][r2]-sum[l1-1][r2]-sum[l2][r1-1]+sum[l1-1][r1-1];}
void Main()
{
    n=read(),m=read();
    for(int i=0;i<=n;i++)for(int j=0;j<=n;j++)a[i][j]=sum[i][j]=0;
    bool ff=1;
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        a[x][y]=1;
        if(x==y)ff=0;
    }
    if(!ff){puts("0");return ;}
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
        }
    }
    memset(dp,0,sizeof dp);
    for(int i=1;i<=n;i++)dp[i][i]=1;
    for(int len=2;len<=n;len++)
    {
        for(int l=1;l<=n-len+1;l++)
        {
            int r=l+len-1;
            if(!ask(l,l+1,l,r))(dp[l][r]+=dp[l+1][r])%=mod;
            if(!ask(l+1,l,r,l))(dp[l][r]+=dp[l+1][r])%=mod;
            for(int k=l+1;k<r;k++)
            { 
                if(!ask(l,l+1,l,k)&&!ask(k+1,l,r,k))
                {
                    dp[l][r]=(dp[l][r]+dp[l+1][k]*dp[k+1][r]%mod)%mod;
                }
            }
        }
    }
    printf("%lld\n",dp[1][n]);
}
signed main()
{
#ifdef Lydic
    freopen(".in", "r", stdin);
    freopen(".out", "w", stdout);
//  #else
//      freopen("Stone.in","r",stdin);
//      freopen("Stone.out","w",stdout);
#endif
    int T=read();
    while(T--)Main();
    return 0;
}