[题解] permutation
[题解] Permutation
解析
一眼 DP 或者 组合。
70pts
场上推的DP
对于 ,先把所有序列枚举出来:
可以发现,对于分割线上的部分,可以看作 的所有序列每个数 ,然后前导一个 。因为答案计算的是绝对值,所以每个数加一相当于整体抬高,不影响答案。而对于分割线下的部分,可以看作 的所有序列每个数 ,同样不影响答案。令 表示对应 的答案。那么有:
留个问号是因为,这两部分合并还可以产生贡献,为上面部分最后一个数与下面部分第一个数的差的绝对值。上面部分最后一个数为 ,下面部分第一个数为 。所以有:
但 都是 级别的,空间会炸。可以发现 只和 有关,可以滚掉。 和 同减,并且 ,那么只维护最小的 即可。
场码
#include<bits/stdc++.h> using namespace std; constexpr int B = 1 << 23; char buf[B], *p1 = buf, *p2 = buf; #define gt() (p1==p2 && (p2=(p1=buf)+fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++) template <typename T> inline void rd(T &x){ x = 0; int f = 0; char ch = gt(); for(; !isdigit(ch); ch = gt()) f ^= ch == '-'; for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48); x = f ? -x : x; } char obuf[B], *O = obuf; #define pt(ch) (O-obuf==B && (fwrite(obuf, 1, B, stdout), O=obuf), *O++=(ch)) template <typename T> inline void wt(T x){ if(x < 0) pt('-'), x = -x; if(x >= 10) wt(x / 10); pt(x % 10 ^ 48); } #define fw fwrite(obuf, 1, O - obuf, stdout) #define ll long long #define ull unsigned long long constexpr int N = 1e6 + 5, M = 1e9 + 7; int n, k, m, f[2][N], lst, now = 1; int main(){ // freopen("perm.in", "r", stdin); // freopen("perm.out", "w", stdout); rd(n), rd(k), rd(m); if(n == k) wt(m); else { int tmp = k - m; for(int i=tmp; i<=n; ++i){ for(int j=max(1, m+i-n); j+tmp<=i; ++j){ if(j == 1) f[now][j] = i - (j + tmp); else if(i == j + tmp) f[now][j] = 0; else f[now][j] = ((ll)f[lst][j] + (ll)f[lst][j-1] + abs(i - j - tmp - 1)) % M; } for(int j=max(i, m+i-1-n); j+tmp<=i-1; ++j) f[lst][j] = 0; now ^= 1, lst ^= 1; } wt(f[lst][m]); } return fw, 0; }
另一种 DP 思路
先打表找规律。打出 以内的表可以发现,答案只和 和 相关,那么把 设为横坐标, 设为纵坐标,于是有:
令 表示图上对应横坐标与纵坐标的值。于是有:
注意边界判断:
答案即为:
于是 递推即可。
100pts
考虑优化刚才的那个 DP,可以发现,对于点 的答案可以看作是从这个点一直走到到点 的所有路径的权值总和,每个点的权值即为这个点所在的纵列编号(纵坐标)。这个东西可以用组合求解。可以枚举每一个点 ,一共有 条路径经过这一点。对于边界 需要特判一下,看作权值为 。于是可以列出式子:
但是这个式子是 的。所以考虑优化。有一个公式:
用上面的式子套公式即可优化到一维:
复杂度 。
code
#include<bits/stdc++.h> using namespace std; constexpr int N = 1e6 + 5, M = 1e9 + 7; int n, m, k, ans, p[N<<1], inv[N<<1]; #define ll long long inline int qpow(int a, int k){ int as = 1; while(k){ if(k & 1) as = (ll)as * a % M; a = (ll)a * a % M; k >>= 1; } return as; } inline int C(int a, int b){ return (ll)p[a] * inv[b] % M * inv[a-b] % M; } int main(){ freopen("perm.in", "r", stdin); freopen("perm.out", "w", stdout); ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>m>>k; if(n == m) return cout<<m, 0; n -= m + 1; p[0] = 1; for(int i=1; i<=n+m; ++i) p[i] = (ll)p[i-1] * i % M; inv[n+m] = qpow(p[n+m], M-2); for(int i=n+m-1; i>=0; --i) inv[i] = (ll)inv[i+1] * (i+1) % M; if(k >= 2) for(int j=0; j<=n; ++j) ans = ((ll)ans + (ll)(n-j) * C(j+k-1, j+1) % M) % M; for(int j=0; j<=n; ++j) ans = ((ll)ans + (ll)C(k+j-1, j)) % M; return cout<<ans, 0; }
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18374557
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步