1. 程式人生 > 其它 >http-server 不是內部或外部命令,也不是可執行的程式或批處理檔案

http-server 不是內部或外部命令,也不是可執行的程式或批處理檔案

028D Chords

題目描述

點此看題

解法

首先考慮把問題轉化到序列上,可以看成序列上的兩點匹配,如果匹配形成的區間相交則看成有邊。

一個關鍵的 \(\tt observation\) 是:任意聯通塊一定可以被某個區間完全包含,並且這個區間的兩個端點都在連通塊內。這說明我們可以通過列舉區間的形式來列舉連通塊,設 \(f(i,j)\) 表示區間 \([i,j]\) 任意連邊(但是不能連出去),最後 \(i,j\) 在同一連通塊內的方案數,那麼可以直接列舉連通塊來算貢獻:

\[ans=\sum f(i,j)\cdot g(2n-m-c(i,j)) \]

其中 \(g(x)=1\cdot 3\cdot 5\ ...\ (x-1)\)

表示 \(x\) 個點的序列任意連邊的方案數,\(c(i,j)\) 表示區間 \([i,j]\) 之間未匹配點的個數。

現在考慮 \(f(i,j)\) 的轉移,正難則反,首先讓 \(f(i,j)=g(c(i,j))\),然後減去 \(i,j\) 不在同一連通塊的方案,這裡我們可以列舉 \(i\) 真實連通塊的右端點是 \(k\),那麼就找到了子問題:

\[f(i,j)=g(c(i,j))-\sum_{k=i}^{j-1}f(i,k)\cdot g(c(k+1,j)) \]

時間複雜度 \(O(n^3)\)

#include <cstdio>
const int M = 605;
const int MOD = 1e9+7;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans,mt[M],f[M][M],g[M];
signed main()
{
	n=read()<<1;m=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		mt[x]=y;mt[y]=x;
	}
	g[0]=1;
	for(int i=2;i<=n;i+=2)
		g[i]=g[i-2]*(i-1)%MOD;
	for(int i=n;i>=1;i--) for(int j=i+1;j<=n;j+=2)
	{
		int ok=1,c=j-i+1;
		for(int k=i;k<=j;k++)
			if(mt[k] && (--c,mt[k]<i || mt[k]>j)) ok=0;
		if(!ok) continue;
		f[i][j]=g[c];
		for(int k=j,d=0;k>i;k--)
			d+=!mt[k],f[i][j]=(f[i][j]-f[i][k-1]*g[d])%MOD;
		ans=(ans+f[i][j]*g[n-2*m-c])%MOD;
	}
	printf("%lld\n",(ans+MOD)%MOD);
}