[CF888F]Connecting Vertices

题目

传送门

题解

这道题如果不仔细分析,还真看不出是道区间 \(DP\) 的题...

首先,题目要求的其中一个不能连边的限制有些奇怪:

两条线段不能在顶点之外的地方相交,如不能同时连(1,3) 和 (2,4)

如果我们直接放在一个 \(N\) 边形上,思考这个条件指哪些点无法连接?

\(N=5\) 举例,我们无法同时连接

  1. \((1,3)\)\((2,4),(2,5),(2,6)\)
  2. \((1,4)\)\((2,5),(3,5)\)
  3. \((1,5)\) 似乎可以不与其他的排斥
  4. ......

经过分析,发现:如果可以连接 \((a,b)\)\((c,d)\),那么必然有 \([a,b]\)\([c,d]\) 不相交(后面的 \([]\) 指区间)

再结合 \(N\le 500\),不难想到这题可以用区间 \(DP\)

\(1...N\) 放到一个区间上,所谓连边就是选择某一个区间,而我们的目的就是选择 \(N-1\) 个区间,让它们端点相连,保证最后所有点联通且选择的区间不能相交且选择的所有区间的左、右端点需有 \(a[l][r]=1\)

定义 \(f[l][r]\) 表示将 \([l,r]\) 这个区间的点全部联通的方案数,那么考虑两种转移:

  1. 如果 \(l,r\) 可以连接即有 \(a[l][r]=1\),那么考虑枚举一个断点 \(i\),让 \(i\)\(i+1\) 断开,让 \([l,i]\)\([i+1,r]\) 通过边 \((l,r)\) 联通,则 \(f[l][r]=\sum_{i=l}^{r-1}f[l][i]\times f[i+1][r]\)
  2. 考虑 \(l,r\) 是通过某一个中转点连接而非直接连接,枚举这个中转点 \(i\),有转移 \(f[l][r]=\sum_{i=l}^{r-1}f[l][i]\times f[i][r]\)

显然最后的答案即为 \(f[1][n]\),但是,对于第二个状态的转移显然是存在问题的:

首先观察状转 \(f[l][r]=\sum_{i=l}^{r-1}f[l][i]\times f[i][r]\),而对于 \(f[i][r]\),由于定义显然它包含了连接 \([i,r]\)\(p(p\in [i,r)\) 为中转点的情况,而当 \(i=p\) 时,我们又重新算了一遍以这个点为中转点的情况,显然这两个方案重复。

那么,如何去掉这种重复的情况?随便加上一些不同的地方,让其变得不同即可

针对我们的状态定义,最方便的一种就是再加上一维 \([0/1]\),整体 \(f[l][r][0/1]\) 表示将 \([l,r]\) 全部联通,其中 \(l,r\) 两个点强制 连/不连 的情况,显然原状态 \(f'[l][r]=f[l][r][0]+f[l][r][1]\),对于转移有

  1. 由于我们之前的转移是假定连接了 \(l,r\),那么这个状态不变,只是将 \(f'[l][r]\) 展开即可,变为 \(f[l][r][0]=\sum_{i=l}^{r-1}(f[l][i][0]+f[l][i][1])\times (f[i][r][0]+f[i][r][1])\)
  2. 这个状态如果沿用之前的是有问题,这个我们说过,那么强制一些东西不一样,强制连接 \(l,i\),那么前后的状态就会有所不同,状转 \(f[l][r][1]=\sum_{i=l}^{r-1}f[l][i][0]\times (f[i][r][0]+f[i][r][1])\)

我们最后求的是 \(f'[l][r]=f[l][r][0]+f[l][r][1]\)

代码

#include<cstdio>
#include<cstring>

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long

#define cg (c=getchar())
template<class T>inline void read(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T>inline T read(const T sample){
    T x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXN=500;
const int MOD=1e9+7;

int a[MAXN+5][MAXN+5],n;

inline void Init(){
    n=read(1);
    rep(i,1,n)rep(j,1,n)a[i][j]=read(1);
}

int f[MAXN+5][MAXN+5][2];
int Dfs(const int l,const int r,const int q){
    if(l==r){
        if(q==0)return 1;
        return 0;
    }
    if(f[l][r][q]!=-1)return f[l][r][q];
    f[l][r][q]=0;
    if(q==0){
        if(a[l][r])rep(i,l,r-1){
            f[l][r][q]+=1ll*(Dfs(l,i,0)+Dfs(l,i,1))*(Dfs(i+1,r,0)+Dfs(i+1,r,1))%MOD;
            if(f[l][r][q]>=MOD)f[l][r][q]-=MOD;
        }
    }else{
        rep(i,l,r-1)if(a[l][i]){//注意, 此处必须是 r-1, 不然就会算上强制连接 l,r 的情况
            f[l][r][q]+=1ll*Dfs(l,i,0)*(Dfs(i,r,0)+Dfs(i,r,1))%MOD;
            if(f[l][r][q]>=MOD)f[l][r][q]-=MOD;
        }
    }return f[l][r][q];
}

signed main(){
    Init();
    memset(f,-1,sizeof f);
    printf("%d\n",(Dfs(1,n,0)+Dfs(1,n,1))%MOD);
    return 0;
}
posted @ 2020-07-29 10:17  Arextre  阅读(105)  评论(0编辑  收藏  举报