10 15
考挂了 中午和某位强者去吃饭他说他状态不好 可能会挂 结果又是快Ak 了 然后我挂了 这就是 强者定律吧说着我要挂了 然后别人挂了...
今天得分 64 分T1 52 分 T3 12 分。丢人 and 很难受 也让我看清了生活的真相。世界上只有一种英雄主义就是 认清生活的真相之后依然还热爱生活。
有m个括号坏掉了 考虑组合 16分 爆搜 32 分dp 如何dp?这个dp 和普通的 括号匹配方案数即卡特兰数n^2递推不一样因为不需要记录还有多少个右括号已经用过了。
但是m个括号坏掉了 所以右括号也要记录个数 怎样能把整个括号序列都表达出来呢? 很有趣的问题 可以设 f[i][j][k] 表示已经形成了j个匹配 有效左括号为j 还剩下k个右括号的方案数。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define INF 1000000000 #define ll long long #define db double #define pii pair<ll,ll> #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+j<n)f[i][j+1][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; }
100分的话考虑折线法。我理解不够深刻 这里不敢赘述,
#include<bits/stdc++.h> using namespace std; typedef long long ll; template<typename T>inline 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; }
样例有点毒瘤其实 但是影响不大 原地爆炸qwq.算了十几分钟 觉得很毒瘤。
但是还是可以写的 考虑dp f[i][j] 表示以i为根的子树种大小为j的联通块期望混乱度 。 转移有点繁琐 这里不再赘述 是n^3的。
然后不会了 咕掉 不太能理解这个东西的思想。
考虑8分爆搜 然后观察题目显然的是序列必然为 奇数 且 有一定的 性质例如一个数一定比上上一个数大或者小什么的。
f[i][j][k] 考虑到了 第i个数且上一个数字是j 上上数字是k 的方案数。转移n^3 显然可以滚动数组。
不懂 咕掉。