10 15

考挂了 中午和某位强者去吃饭他说他状态不好 可能会挂 结果又是快Ak 了 然后我挂了 这就是 强者定律吧说着我要挂了 然后别人挂了...

今天得分 64 分T1 52 分 T3 12 分。丢人 and 很难受 也让我看清了生活的真相。世界上只有一种英雄主义就是 认清生活的真相之后依然还热爱生活。

10 15_第1张图片

10 15_第2张图片

有m个括号坏掉了 考虑组合 16分 爆搜 32 分dp 如何dp?这个dp 和普通的 括号匹配方案数即卡特兰数n^2递推不一样因为不需要记录还有多少个右括号已经用过了。

但是m个括号坏掉了 所以右括号也要记录个数 怎样能把整个括号序列都表达出来呢? 很有趣的问题 可以设 f[i][j][k] 表示已经形成了j个匹配 有效左括号为j 还剩下k个右括号的方案数。

//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include<string>
#include
#include
#include
#include
#include
#include<set>
#include
#define INF 1000000000
#define ll long long
#define db double
#define pii pair
#define mk make_pair
#define us unsigned
#define mod 998244353
#define R register
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=2000010,maxn=110;
int n,m,N,w,s;
ll jc[MAXN],inv[MAXN],in[MAXN],sum,cnt;
ll f[maxn][maxn][maxn];//f[i][j][k]成功匹配了i个 此时还有j个有效左括号 还有k个右括号
inline ll ksm(ll b,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)ans=ans*b%mod;
        b=b*b%mod;
        p=p>>1;
    }
    return ans;
}
inline void prepare()
{
    jc[0]=1;
    for(int i=1;i<=N;++i)jc[i]=jc[i-1]*i%mod;
    inv[N]=ksm(jc[N],mod-2);
    for(int i=N-1;i>=0;--i)inv[i]=inv[i+1]*(i+1)%mod;
    for(int i=1;i<=N;++i)in[i]=inv[i]*jc[i-1]%mod;
}
inline int Ca(int a,int b)//求Ca(n,m) a!/(a-b)!/b!/b+1
{
    return (((jc[a]*inv[b]%mod)*inv[a-b])%mod)*in[b+1]%mod;
}
inline int C(int a,int b)//求 C(n,m)
{
    return ((jc[a]*inv[b]%mod)*inv[a-b]%mod);
}
int main()
{
    //freopen("1.in","r",stdin);
    freopen("excatalan.in","r",stdin);
    freopen("excatalan.out","w",stdout);
    n=read();m=read();N=n<<1;w=n-m;
    prepare();
    if(!m){printf("%d\n",Ca(N,n));return 0;}
    if(n<=1000)
    {
        f[0][0][n]=1;
        for(R int i=0;i<=w;++i)
            for(R int j=0;j<=n;++j)
                for(R int k=n;k>=0;--k)
                {
                    if(i+j1][k]=(f[i][j+1][k]+f[i][j][k])%mod;
                    if(k&&!j)f[i][j][k-1]=(f[i][j][k-1]+f[i][j][k])%mod;
                    if(j&&k)f[i+1][j-1][k-1]=(f[i+1][j-1][k-1]+f[i][j][k])%mod;
                }
        printf("%lld\n",f[w][m][0]);
        return 0;
    }
    return 0;
}
View Code

100分的话考虑折线法。我理解不够深刻 这里不敢赘述,

#include
using namespace std;
typedef long long ll;
templateinline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
} 
const ll mod=998244353;
ll fac[2002002],inv[2002002],n,m,ans;
inline ll pow(ll a,ll b) {
    ll res=1;
    while(b) {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
inline ll C(ll n,ll m) {
    if(m>n) return 0;
    if(m<0) return 0;
    if(n<0) return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
inline ll c(ll n) {return (C(2ll*n,n)-C(2ll*n,n+1)+mod)%mod;}
ll exctl(int n,int m) {return (C(2ll*n,n)-C(2*n,n-m-1)+mod)%mod;}
int main() {
    freopen("excatalan.in","r",stdin);
    freopen("excatalan.out","w",stdout);
    read(n); read(m); 
    fac[0]=1;
    for(int i=1;i<=2*n;i++) {
        fac[i]=fac[i-1]*i;
        fac[i]%=mod;
    }
    inv[2*n]=pow(fac[2*n],mod-2);
    for(int i=2*n-1;i>=0;i--) {
        inv[i]=inv[i+1]*(i+1);
        inv[i]%=mod;
    }
    if(m==0) ans=c(n);
    else ans=(exctl(n,m)-exctl(n,m-1)+mod)%mod;
    printf("%lld\n",ans);
    return 0;
}
View Code

10 15_第3张图片

10 15_第4张图片

样例有点毒瘤其实 但是影响不大 原地爆炸qwq.算了十几分钟 觉得很毒瘤。

但是还是可以写的 考虑dp f[i][j] 表示以i为根的子树种大小为j的联通块期望混乱度 。 转移有点繁琐 这里不再赘述 是n^3的。

然后不会了 咕掉 不太能理解这个东西的思想。

10 15_第5张图片

10 15_第6张图片

10 15_第7张图片

考虑8分爆搜  然后观察题目显然的是序列必然为 奇数 且 有一定的 性质例如一个数一定比上上一个数大或者小什么的。

f[i][j][k] 考虑到了 第i个数且上一个数字是j 上上数字是k 的方案数。转移n^3 显然可以滚动数组。

不懂 咕掉。

10 15_第8张图片

你可能感兴趣的:(10 15)