【题解】P7967 [COCI 20212022 #2] Magneti

P7967 [COCI 2021/2022 #2] Magneti

题意

给定 \(n\) 个磁铁和 \(l\) 个空位,其中相邻空位之间的距离为 \(1\),每个空位可放置一个磁铁。所有 \(n\) 个磁铁都必须被放置。每个磁铁可以吸引距离小于 \(r_i\) 的其它磁铁。

求所有磁铁互不吸引的方案总数对 \(10^9+7\) 取模的结果。

题解

知识点:动态规划。

启发:

  • 连续段 dp。

连续段 dp,这题几乎是板子了。

发现这个半径限制比较烦,所以考虑将磁铁从小到大排序,同理从小到大插入,这样只需要考虑后插入的磁铁的半径限制。

在这题,定义连续段为连续的、卡满半径限制的若干个磁铁。定义磁铁所占的总空位个数为,磁铁本身占的位子个数,以及磁铁之间由于半径限制被迫空开的若干空位个数之和。

\(dp_{i,j,k}\) 表示插入了前 \(i\) 个磁铁,形成了 \(j\) 个连续段,总共占了 \(k\) 个空位的方案数。

套路地,列出以下三种转移:

\(dp_{i,j,k}\times (j+1) \to dp_{i+1,j+1,k+1}\),表示第 \(i+1\) 个磁铁自己新开一个连续段,关于为什么 \(k\to k+1\),因为它自成一个连续段,所占的空位只有它自己本身,没有同连续段的磁铁和它互相限制。

\(dp_{i,j,k}\times 2\times j\to dp_{i+1,j,k+a_{i+1}}\),表示第 \(i+1\) 个磁铁接在某个连续段的一边(每个连续段有两边)。

\(dp_{i,j,k}\times (j-1)\to dp_{i+1,j-1,k+2\times a_{i+1}-1}\),表示第 \(i+1\) 个磁铁合并两个连续段,需要保证 \(j\ge 2\)

注意转移的边界,转移后的 \(k\le l\)

对于上述 dp 的方案,由于处理的都是磁铁之间紧挨着的情况,显然可以让磁铁之间继续空开一些距离以拓展更多的方案。

对于一个占用了 \(k\) 个空位的方案,有 \(n+1\) 个位置可以继续去空位出来(磁铁之间 \(n-1\) 个加上边界 \(2\) 个),将 \(l-k\) 个空位分配给他们,由隔板法可知,可以拓展出 \(\binom{l-k+n}{n}\) 种方案。

所以答案为 \(\displaystyle \sum_{i=0}^l dp_{n,1,i}\times \binom{l-k+n}{n}\)

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()

#define N 55
#define L 11451
#define int long long

const int mod=1e9+7;
int n,l,a[N],dp[N][N][L],fac[L],iv[L];

inline int qpow(int a,int b=mod-2){
    int ans=1;

    while(b){
        if(b&1){
            ans=ans*a%mod;
        }
        a=a*a%mod;
        b>>=1;
    }

    return ans;
}

inline void init(int lim){
    fac[0]=1;
    rep(i,1,lim){
        fac[i]=fac[i-1]*i%mod;
    }

    iv[lim]=qpow(fac[lim]);

    per(i,0,lim-1){
        iv[i]=iv[i+1]*(i+1)%mod;
    }
}

inline int C(int n,int m){
    if(n<m){
        return 0;
    }

    return fac[n]*iv[m]%mod*iv[n-m]%mod;
}

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

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    init(11111);

    cin>>n>>l;

    rep(i,1,n){
        cin>>a[i];
    }

    sort(a+1,a+1+n);

    dp[0][0][0]=1;

    rep(i,0,n-1){
        rep(j,0,i){
            rep(k,0,l){
                int re=dp[i][j][k];

                if(k+a[i+1]<=l){
                    add(dp[i+1][j+1][k+1],re*(j+1)%mod);
                }

                if(k+a[i+1]<=l){
                    add(dp[i+1][j][k+a[i+1]],re*j*2%mod);
                }

                if(k+2*a[i+1]-1<=l&&j>=2){
                    add(dp[i+1][j-1][k+2*a[i+1]-1],re*(j-1)%mod);
                }
            }
        }
    }

    int ans=0;

    rep(i,0,l){
        ans=(ans+dp[n][1][i]*C(l-i+n,n)%mod)%mod;
    }

    cout<<ans;

    return 0;
}
posted @ 2025-08-08 20:14  Lucyna_Kushinada  阅读(19)  评论(0)    收藏  举报