1 条题解
-
0
以下最大值均指严格最大值。
思路
首先考虑如果没有 的限制,那么答案显然是 。
那么考虑每个二元限制 本质上限制了什么条件。
仔细读题:
其中奶牛 是第一头比奶牛 到 拥有严格更高牲任力分数的奶牛。
使用瞪眼法可以猜出以下两个结论:
- 第 号奶牛为前缀最大值。
- 第 到第 号奶牛不存在前缀最大值。
::::info[证明]
结论
如果存在 ,使得第 号奶牛的属性值大于等于 号奶牛,那么显然不满足题目的限制。
结论
如果存在 ,使得 号奶牛为前缀最大值,那么第一头比奶牛 到 拥有严格更高牲任力分数的奶牛应该为 而不是 。 ::::
由此还可以得出,将限制按照 排序后,若存在 ,那么无解。不过题目保证有解,所以不存在这种情况。
当出现相同的 时,该位的 应该取最小值,原因是如果满足 较小的限制,那么 较大的也一定满足。实现
将限制按照 从小到大排序。
注意到 ,复杂度为 的算法能通过这道题。
考虑动态规划。
设 表示考虑到前 头奶牛,第 头奶牛的属性值恰为 的方案数。
设 表示考虑到前 头奶牛,第 头奶牛的属性值小于等于 的方案数。 即 的前缀和。考虑转移。
两个限制都与前缀最大值有关。考虑枚举 到 的前缀最大值为 。
可列出状态转移方程:
$$f_{i,j}=\sum_{k=1}^{j-1}(dp_{i-1,k}k^{a_i-h_{i-1}}-dp_{i-1,k-1}(k-1)^{a_i-h_{i-1}})k^{(h_i-1-a_i)}$$注意到 只比 多了一项,所以用前缀和以及快速幂优化一下即可做到 。 ::::info[转移方程看不懂的看这] 分别考虑各部分的贡献。
对于区间 ,贡献为
$$dp_{i-1,k}k^{a_i-h_{i-1}}-dp_{i-1,k-1}(k-1)^{a_i-h_{i-1}}$$注意这里用了一个小小的容斥。因为要保证 在 中出现过,所以用全部小于等于 的方案数(即 )减去全部小于 的方案数(即 ),就能得到 全部小于等于 且至少存在一个 的方案数。
对于区间 的贡献,每一位都可以任意取小于等于 的数,显然为 。
二者乘法原理即可得到上面的转移方程。 :::: 别忘了初始化 均为 。
代码
马蜂良好有注释。#include<bits/stdc++.h> using namespace std; #define ull unsigned long long #define ll long long #define ld long double #define dd double //char buf[1<<23],*p1=buf,*p2=buf; //#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++) inline ll read() { ll x = 0, f = 1; char ch; while (((ch = getchar()) < 48 || ch > 57) && ch != EOF)if (ch == '-')f = -1; if (ch == EOF)x = EOF; while (ch >= 48 && ch <= 57)x = x * 10 + ch - 48, ch = getchar(); return x * f; } char __sta[1009], __len; inline void write(ll x, ll bo) { if (x < 0)putchar('-'), x = -x; do __sta[++__len] = x % 10 + 48, x /= 10; while (x); while (__len)putchar(__sta[__len--]); if (bo == 3)return; putchar(bo ? '\n' : ' '); } constexpr unsigned int N=1e4+9,M=109,MOD=1e9+7; int n,q,c; ll dp[M][N]; //考虑前 h[i] 个位置,第 h[i] 位 <= j 的方案数 ll f[M][N]; //考虑前 h[i] 个位置,第 h[i] 位刚好为 j 的方案数 int h[N]; map<int,int>a; //qp int qp(ll a,int b){ ll ans=1; while(b){ if(b&1)ans=ans*a%MOD; a=a*a%MOD,b>>=1; } return ans; } //input void input(){ n=read(),q=read(),c=read(); for(int i=1;i<=q;i++){ int x=read(); h[i]=read(); if(a[h[i]]!=0)a[h[i]]=min(a[h[i]],x); else a[h[i]]=x; } sort(h+1,h+q+1); q=unique(h+1,h+q+1)-h-1; } //solve void solve(){ for(int j=0;j<=c;j++){ dp[0][j]=1; } for(int i=1;i<=q;i++){ for(int j=i+1;j<=c;j++){ f[i][j]+=f[i][j-1];//1~a[i] 的前缀最大值小于 j-1 的情况 f[i][j]+=(dp[i-1][j-1]*qp(j-1,a[h[i]]-h[i-1])%MOD-dp[i-1][j-2]*qp(j-2,a[h[i]]-h[i-1])%MOD+MOD)%MOD*qp(j-1,h[i]-1-a[h[i]])%MOD; //1~a[i] 的前缀最大值为 j-1 的情况 f[i][j]%=MOD; dp[i][j]=(dp[i][j-1]+f[i][j])%MOD; } } ll ans=dp[q][c]*qp(c,n-h[q])%MOD; write(ans,1); } //debug void debug(){ for(int i=1;i<=q;i++){ for(int j=1;j<=c;j++){ cout<<i<<' '<<j<<endl<<f[i][j]<<endl; } } } int main() { input(); solve(); return 0; }
- 1
信息
- ID
- 7631
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 32
- 已通过
- 8
- 上传者