Atcoder agc039E

考完试才发现E是简单题。。。
思考一下怎么样是合法的生成树。不妨设 1 1 1号点跟 k k k号点匹配,那么对于区间 [ 2 , k − 1 ] [2,k-1] [2,k1] [ k + 1 , n ] [k+1,n] [k+1,n],我们会各自按顺序分成 c c c段,每一段内部有奇数个点,其中其他点内部匹配,伸出一个点跨越中间的边跟另一边的一段伸出的一个点匹配,并且匹配的段是按跟 1 1 1号点距离一一对应的。
那么看起来就可以DP了,我们设 F [ i ] [ j ] [ k ] F[i][j][k] F[i][j][k]表示区间 [ i , j ] [i,j] [i,j](长度为奇数)内部其他点两两匹配,伸出的点为 k k k(其他匹配要跟 k k k伸出的边连通)的方案数,转移的时候直接枚举 i i i j j j所在的段的长度和伸出的点即可得到一个多项式做法。
在转移的同时维护 G [ i ] [ j ] [ k ] G[i][j][k] G[i][j][k]表示区间外一点 k k k跟区间 [ i , j ] [i,j] [i,j]内部某点匹配, [ i , j ] [i,j] [i,j]内部其他点两两匹配(同样要连通)的方案数,就可以加速转移了。时间复杂度是 O ( n 5 ) \mathcal O(n^5) O(n5),题解给出了吓人(不知道是啥)的 O ( n 8 ) \mathcal O(n^8) O(n8)做法。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

char str[45][45];

ll f[45][45][45],g[45][45][45];

int main() {
  int n;
  scanf("%d",&n);
  n=2*n;
  for(int i=1;i<=n;i++) scanf("%s",str[i]+1);
  for(int i=1;i<n;i++) {
    f[i][i][i]=1;
    for(int j=i+1;j<=n;j++)
      if (str[i][j]=='1') g[i][i][j]=1;
  }
  for(int i=3;i<n;i+=2)
    for(int j=1;i+j<=n;j++) {
    	int k=i+j-1;
    	for(int l=j;l<=k;l+=2)
    	for(int r=k;r>l;r-=2) {
    		ll s=0;
    		for(int t=r;t<=k;t++) s+=g[j][l][t]*f[r][k][t];
    		for(int t=l+1;t<r;t++) f[j][k][t]+=f[l+1][r-1][t]*s;
		}
		for(int t1=j+1;t1<k;t1++)
		for(int t2=k+1;t2<n;t2++)
		  if (str[t1][t2]=='1') g[j][k][t2]+=f[j][k][t1];
	}
  ll ans=0;
  for(int i=1;i<n;i++)
    if (str[i][n]=='1') ans+=f[1][n-1][i];
  printf("%lld\n",ans);
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值