http-server 不是內部或外部命令,也不是可執行的程式或批處理檔案
阿新 • • 發佈:2022-03-18
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)\)
現在考慮 \(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); }