P10138 [USACO24JAN] Cowmpetency G 题解
有一个长度为 \(N\) 的不确定的数组,每个位置可以填 \([1,C]\) 的整数,满足 \(Q\) 条限制 \((a_i,h_i)\) 使得第 \(h_i\) 的分数严格大于 \([1,a_i]\) 的分数,求满足条件的数组的方案数模 \(10^9+7\) 的结果。
看到计算方案数,想到 dp。比较直观的考虑 dp 状态设当前位置 \(i\),填数字 \(j\) 的方案。但根据做过银组那个题的经验,主要限制是最大值,这样非常难转移。而且有性质 \(a_{next}\geq h\),所以按 \(a\) 排序后可以按顺序处理性质。所以重新定义状态,设 \(dp_{k}\) 表示已经处理前缀限制后最大值是 \(j\) 的方案。
根据经验,这个一个限制实际上就是三部分,即满足区间 \([a+1,h-1]\) 的数小于等于 \(M\),\(c_h>M\),其中 \(M=\max(c_1,c_2,\dots,c_a)\)。所以转移也是三部分:
- 对于区间 \([1,a]\),这些地方可以随便填,没有任何限制。考虑长度为 \(l\),那么又分为两种情况:
- 新的最大值和原来一样是 \(k\),那么这 \(l\) 个都要 \(\leq k\),方案数就是 \(k^l\)。
- 新最大值 \(k\) 满足 \(j<k\),这 \(l\) 个位置要 \(\leq k\),但至少要有一个位置是 \(k\),方案数就是 \(k^l-(k-1)^l\)。
- 总结下来这一段转移就是 $$ndp_k=dp_k\cdot k^l +\left(\sum_{j<k}{dp_j}\right)\cdot\left(k^l -(k-1)^l\right)$$
- 对于区间 \([a+1,h-1]\),这些地方都不能超过最大值 \(k\),所以每个数都 \(\leq k\),方案数是 \(k^l\)。
- 所以转移就是 $$ndp_k=dp_k\cdot k^l$$
- 对于 \(h\) 这个点,必须严格大于最大值。
- 所以转移就是 $$ndp_k=\sum_{j<k}{dp_j}$$
实际上求和的部分都可以使用前缀和优化,再加上快速幂,复杂度可以做到 \(O(QC\log N)\)。
代码还是非常好写的。
#include<bits/stdc++.h>
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)
using namespace std;
typedef long long i64;
typedef __int128 i128;
const int N=1e5+5,M=1e9+7;
void solve();
int n,q,C;
vector<pair<int,int>> p;
vector<i64> dp;
signed main(){
int Test=1;
// scanf("%d",&Test);
while(Test--) solve();
return 0;
}
i64 pw(int a,int b){
if(b==0) return 1;
i64 res=pw(a,b/2)%M;
res=(res*res)%M;
if(b%2) res=(res*a)%M;
return res;
}
void upd1(int l){
if(!l) return;
vector<i64> fact(C+1);
L(i,0,C,1) fact[i]=pw(i,l);
vector<i64> ndp(C+1,0);
i64 pre=dp[0];
L(i,1,C,1){
i64 e=(fact[i]-fact[i-1]+M)%M;
ndp[i]=((dp[i]*fact[i])%M+(pre*e)%M)%M;
pre=(pre+dp[i])%M;
}
dp.assign(ndp.begin(),ndp.end());
}
void upd2(int l){
if(!l) return;
vector<i64> fact(C+1);
L(i,0,C,1) fact[i]=pw(i,l);
vector<i64> ndp(C+1,0);
L(i,1,C,1){
ndp[i]=(dp[i]*fact[i])%M;;
}
dp.assign(ndp.begin(),ndp.end());
}
void upd3(){
vector<i64> ndp(C+1,0);
i64 pre=dp[0];
L(i,1,C,1){
ndp[i]=pre;
pre=(pre+dp[i])%M;
}
dp.assign(ndp.begin(),ndp.end());
}
void solve(){
scanf("%d%d%d",&n,&q,&C);
p.resize(q);
L(i,0,q-1,1) scanf("%d%d",&p[i].first,&p[i].second);
sort(p.begin(),p.end());
dp.assign(C+1,0);
dp[0]=1;
int lasth=0;
for(auto [a,h]:p){
if(a<lasth){
if(h==lasth) continue;
else{
printf("0\n");
return;
}
}
upd1(a-lasth);
upd2(h-a-1);
upd3();
lasth=h;
}
i64 ans=0;
L(i,1,C,1) ans=(ans+dp[i])%M;
ans=(ans*pw(C,n-lasth))%M;
printf("%lld\n",ans);
}
本文来自博客园,作者:jess1ca1o0g3,转载请注明原文链接:https://www.cnblogs.com/jess1ca1o0g3/p/22519653

浙公网安备 33010602011771号