P3255 [JLOI2013] 地性生成 题解
update on 2025.3.9:改了毒瘤马蜂
题面大意
给你 \(n\) 座山,对于每座山有高度和关键字两个变量,求让每座山前严格高于它的山至多有关键字个的方案数以及高度相等的山不区分的方案数。
题解
一个较为经典的 trick,容易发现对于任意一座山,比他矮的放哪和他的方案没有关系,于是我们先按高度递减为第一关键字,关键字递增为第二关键字来排序(为什么要设置第二关键字小问中会说)。
第一问
排完序后枚举每一座山,注意到因为设置了第二关键字,所以限制比较紧的山会先插入,那么这座山一定可以插在高度一样的山后面,因为高度都一样,他们的限制还比我紧,我跟着他们插肯定是合法的。
然后注意到假设比这座山更高的山有 \(tot\) 座,当前山的关键字为 \(a\) 那么就会有 \(\min(a,tot)\) 座比他高的山可以让他插在后边。
于是第 \(i\) 座山的方案数就是:\(\min(a,tot)+(i-tot-1)+1\) 种,其中 \(i-tot-1\) 是和第 \(i\) 座一样高的数量,最后的加一是放到最前面。
第二问
稍微难了一点,我们设 \(dp_{i,j}\) 表示第 \(i\) 座山,插入到了 \(j\) 的方案数,然后因为我们设置了第二关键字,所以为了去重,不妨让每一山都插在相同高度山的后面,那么 \(dp_{i,j}\) 就是上一座山插在 \(j\) 或以前的总方案数,这样显然是正确的,而如果上一座山。
如果到了新高度就清空,初值是 \(dp_{i,1}=1\)。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=2011;
struct nb{
int gao,jian;
}a[1010];
int n,dp[1010][1010],sum[1010],ans;
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].gao>>a[i].jian;
a[i].jian--;
}
sort(a+1,a+1+n,[](nb x,nb y){
if(x.gao!=y.gao){
return x.gao>y.gao;
}
return x.jian<y.jian;
});
ans=1;
int tot=0;
for(int i=1;i<=n;i++){
while(a[tot+1].gao>a[i].gao){
tot++;
}
ans=ans*(min(a[i].jian,tot)+i-tot)%mod;
}
cout<<ans<<' ';
tot=0,ans=1;
dp[0][1]=1;
for(int i=1;i<=n;i++){
while(a[tot+1].gao>a[i].gao){
tot++;
}
for(int j=1;j<=n+1;j++){
sum[j]=(sum[j-1]+dp[i-1][j])%mod;
}
if(tot==i-1){
ans=ans*sum[n+1]%mod;
for(int j=1;j<=n+1;j++){
sum[j]=1;
}
}
for(int j=1;j<=min(tot,a[i].jian)+1;j++){
dp[i][j]=sum[j];
}
}
for(int j=1;j<=n+1;j++){
sum[j]=(sum[j-1]+dp[n][j])%mod;
}
cout<<ans*sum[n+1]%mod;
return 0;
}

浙公网安备 33010602011771号