【题解】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;
}

浙公网安备 33010602011771号