Loading

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);
}
posted @ 2026-08-17 14:10  jess1ca1o0g3  阅读(3)  评论(0)    收藏  举报