Atcoder agc028D

该博客探讨了如何解决Atcoder AGC028D问题,指出使用agc039E方法在已知边的情况下存在困难。作者提出每个连通块在圆上对应一个区间,并定义状态F[i][j]表示在考虑点i时,区间[l,i]内有j个点的匹配点在(i,2N]。通过dp转移寻找新连通块,实现O(N^3)的时间复杂度解决方案。" 38944905,1303317,Qt实战:QComboBox自定义外观与样式表应用,"['Qt开发', 'GUI界面设计', '自定义组件']
摘要由CSDN通过智能技术生成

如果使用类似agc039E的方法,由于已经给定了一些边,并不容易讨论。
我们发现一个连通块事实上在圆上对应着一个区间 [ l , r ] [l,r] [l,r] l < r l<r l<r),其中内部的点两两匹配,另外不存在 l < k < r l<k<r l<k<r,使得 [ l , k ] [l,k] [l,k]内部的点两两匹配。于是可以考虑对出度dp,枚举左端点 l l l,设 F [ i ] [ j ] F[i][j] F[i][j]表示当前考虑到 i i i [ l , i ] [l,i] [l,i]中有 j j j个点的匹配点在 ( i , 2 N ] (i,2N] (i,2N]间,转移的时候不允许跟 [ 1 , i ) [1,i) [1,i)之间的点匹配。当 j j j第一次变为 0 0 0时,意味着出现了一个新的连通块,可以更新答案。
时间复杂度 O ( N 3 ) \mathcal O(N^3) O(N3)

#include <bits/stdc++.h>
#define MOD 1000000007

using namespace std;

typedef long long ll;

inline void add(int &x,ll y) {
  x=(x+y)%MOD;
}

ll facd[305];

void pre(int n) {
  facd[0]=1;
  for(int i=1;i<=n;i++) facd[i]=facd[i-1]*(2LL*i-1)%MOD;
}

int num[605];
int f[2][305];

int main() {
  int n,m;
  scanf("%d%d",&n,&m);
  pre(n);
  for(int i=1;i<=m;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	num[x]=y;
  	num[y]=x;
  }
  int ans=0;
  for(int i=1;i<=2*n;i++) {
  	int cur=0;
  	memset(f[cur],0,sizeof(f[cur]));
  	f[cur][0]=1;
  	int sz1=0,sz2=0;
  	for(int j=i;j<=2*n;j++) {
  	  if (num[j]) {
  	  	  if (num[j]>j) sz1++;
  	  	  else if (num[j]<i) break;
  	  	  else sz1--;
		}
	  else {
	  	  sz2++;
	  	  cur^=1;
	  	  memset(f[cur],0,sizeof(f[cur]));
	  	  for(int k=0;k<=min(n,sz2);k++)
	  	    if (f[cur^1][k]) {
	  	    	if (k) add(f[cur][k-1],(ll)f[cur^1][k]*k);
	  	    	add(f[cur][k+1],f[cur^1][k]);
			  }
	  }
	  if (!sz1&&f[cur][0]) {
	  	add(ans,(ll)f[cur][0]*facd[n-m-(sz2>>1)]);
	  	f[cur][0]=0;
	  }
	}
  }
  printf("%d\n",ans);
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值