jddxl
Posted znsbc-13
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jddxl相关的知识,希望对你有一定的参考价值。
遇到一个简单$dp$,觉得很棒在此写下题解
真的非常简单,
jddxl
有一个长度$n$括号序列(只有$"()"$ ),给定其中长度为$m$一段,求满足括号匹配方案数
$n,m<=1e6$ $n-m<=4000$
题解
性质:我们发现一个合法匹配序列左扩号时刻比右括号多(显然),最后左扩号数量等于右括号数量
设$f[i][j]$表示长度为$i$序列,左扩号比右括号多$j$个方案数
那么类似的设$g[i][j]$为右括号比左扩号多$j$的方案数
(其实$f$和$g$值完全一样)
转移非常简单
当前括号可能是$($则贡献$f[i][j]=f[i-1][j-1]$为$)$则$f[i][j]=f[i-1][j+1]$
总贡献$f[i][j]=f[i-1][j-1]+f[i-1][j+1]$
类似的$g[i][j]=g[i-1][j-1]+g[i-1][j+1]$
那么思考统计答案
其实也非常简单
枚举第一段长度$i$,第一段左扩号比右括号多$j$,设给定序列左扩号比右括号多$j$
$ans=sumlimits_{i=1}^{i<=n-m} sumlimits_{j=0}^{j<=i} f[i][j]*g[(n-m)-i][j+tot]$
注意判是否合法
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 4040 const ll mod=1e9+7; char c[2020202]; ll f[A][A]; ll tot,mint,n,m,ans; int main(){ // freopen("da.in","r",stdin); freopen("ans.bf","w",stdout); scanf("%lld%lld",&n,&m); scanf("%s",c+1); for(ll i=1;i<=m;i++){ if(c[i]==‘(‘) tot++; else tot--; if(i==1) mint=tot; else mint=min(mint,tot); } f[0][0]=1; for(ll i=1;i<=n-m;i++){ for(ll j=0;j<=i;j++){ if(j==0) f[i][j]=f[i-1][j+1]; else f[i][j]=(f[i-1][j+1]+f[i-1][j-1])%mod; } } for(ll i=0;i<=n-m;i++){ for(ll j=0;j<=i;j++){ if(j+mint>=0&&j+tot<=n-m) ans=(ans+f[i][j]*f[(n-m)-i][j+tot]%mod)%mod; } } printf("%lld ",ans); }
我没数据,也没法提交,和$std$对拍了一下
下面是我的数据生成及对拍
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main(){ 4 system("g++ bf.cpp -o bf"); 5 system("g++ sol.cpp -o sol"); 6 system("g++ da.cpp -o da"); 7 int rp=0; 8 while(++rp){ 9 cout<<rp<<" "; 10 system("./da"); 11 system("./sol"); 12 system("./bf"); 13 if(system("diff -B -b ans.sol ans.bf")){ 14 puts("WA"); 15 while(1); 16 } 17 puts("AC"); 18 } 19 }
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main(){ 4 freopen("da.in","w",stdout); 5 srand(time(NULL)); 6 7 int m=rand()%10000+300; 8 int c=rand()%m+1; 9 while(m-c>2000){ 10 c=rand()%m+1; 11 } 12 cout<<m<<" "<<c<<endl; 13 for(int i=1;i<=c;i++){ 14 if(rand()%2){ 15 printf("("); 16 } 17 else printf(")"); 18 } 19 cout<<endl; 20 }
以上是关于jddxl的主要内容,如果未能解决你的问题,请参考以下文章