考挂了 中午和某位强者去吃饭他说他状态不好 可能会挂 结果又是快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;
}
View Code
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分爆搜 然后观察题目显然的是序列必然为 奇数 且 有一定的 性质例如一个数一定比上上一个数大或者小什么的。