「计数」客星璀璨之夜 + 大佬

客星璀璨之夜

“虽然不清楚是不是那两人的力量

在那个风暴肆虐的夜晚,的确有一瞬

真的在那一瞬间,在云破天开的时候

透过空隙中看到的璀璨星空,不知为何倒映眼中不能忘怀” ——《奇迹召唤星辰》

原题是HDU 6848,魔改的挺厉害的,看来组题的那位学长也是个东方厨233

考试的时候看不懂题,花了10min打了个30分暴力滚粗,想着回来再看看他。

然后30分就成了最终得分。

做法来自\(sir\)

将问题转化,球与洞间的距离是可枚举的,一共\(\frac{n(n+1)}{2}\)种,我们需要求的是这段距离的总贡献,就需要知道其方案数。

定义: \(dp[i][j]\) 为,所枚举的球与洞之间有\(i\)对球洞,所枚举的洞后面还有\(j\)对球和洞的方案数。

然后转移:

\[dp[i][0] = (2i-1)\times dp[i - 1][0] \]

(有i个球,一个球有两种方向,最靠近枚举的洞的球有一个方向不能走,然后就成了递归子问题)

\[dp[0][j] = (2^j \times j!) + (2j-1) \times dp[0][j - 1] \]

\(2^j \times j!\),即考虑先让所枚举的球去击打所枚举的洞,然后所枚举的洞后面所有球和洞就可以随便打了,方案数就是\(2^j \times j!\)

\[dp[i][j] = (2i-1) \times dp[i - 1][j] + (2j-1) \times dp[i][j - 1] \]

然后枚举每个球和洞,用对应的dp乘上距离即可,注意dp时默认球在前洞在后,所以反向再做一遍。

dp数组的预处理:

void Pre_Work() {
      dp[0][0] = 1;
      pow2[0] = 1;
      for (int i = 1; i <= n; ++i) dp[i][0] = (2 * i - 1) * dp[i - 1][0] % mod, pow2[i] = pow2[i - 1] * 2 % mod;
      for (int j = 1; j <= n; ++j) dp[0][j] = (pow2[j] * jc[j] % mod + (2 * j - 1) * dp[0][j - 1] % mod) % mod;
      for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
	          dp[i][j] = ((2 * i - 1) * dp[i - 1][j] % mod + (2 * j - 1) * dp[i][j - 1] % mod) % mod;
            }
      }
}

统计答案:

for (int i = 2; i <= n; i += 2) { //枚举球
    for (int j = i + 1; j <= n + 1; j += 2) { //枚举洞
          ans = (ans + abs(x[i] - x[j]) * dp[(j - i) / 2][(n + 1 - j) / 2] % mod * pow2[(i - 1) / 2] % mod * A(n / 2, (i - 1) / 2) % mod) % mod;
    }
}
for (int i = n; i >= 2; i -= 2) {
    for (int j = i - 1; j >= 1; j -= 2) {
          ans = (ans + abs(x[i] - x[j]) * dp[(i - j) / 2][j / 2] % mod * pow2[(n + 1 - i) / 2] % mod * A(n / 2, (n + 1 - i) / 2) % mod) % mod;
    }
}
sum = jc[n / 2] * Qpow(2, n / 2) % mod;
ans = ans * Qpow(sum, mod - 2) % mod;

乘上\(2^{\frac{i-1}{2}} \times A_{\frac{n}{2}}^{\frac{i-1}{2}}\) 的原因是:(此时的n已乘2,i为所枚举的球的下标)

我们在考虑DP时没有算上枚举的球之前的球和洞的影响,所以枚举的球之前的球和洞可以在整个过程中的任何一个时刻发生碰撞。

整个过程会发生\(n\)次碰撞,影响就是\(A_{\text{碰撞次数}}^{\text{前面的球和洞发生的碰撞次数}}\),再乘上\(2^{前面的球洞对数}\)

大佬

辣鸡ljh NOI之后就退役了,然后就滚去学文化课了。
他发现katarina大佬真是太强了,于是就学习了一下katarina大佬的做题方法。
比如这是一本有n道题的练习册,katarina大佬每天都会做k道题。
第一天做第\(1……k\)题,第二天做第 \(2 …… k + 1\) 题……第 \(n-k+1\) 天做第\(n-k+1 …… n\)道题。
但是辣鸡 ljh 又不想太累,所以他想知道katarina大佬做完这本练习册的劳累度。
每道题有它的难度值,假设今天katarina大佬做的题目中最大难度为t,那么今天katarina大佬的劳累度就是wt?,做完这本书的劳累值就是每天的劳累值之和。
但是辣鸡ljh一道题都不会,自然也不知道题目有多难,他只知道题目的难度一定在\(1……m\)之间随机。
他想让即将参加 NOIP 的你帮他算算katarina大佬做完这本书的劳累值期望

这题不是秒切吗 ——\(sir\)

这题一定不要去考虑每道题的贡献,这样一道题可能会被扫\(1\)\(n-k+1\)遍不等,没办法计算贡献。

于是考虑每个滑动窗口的贡献,发现对于窗口而言,贡献与所处位置无关,只与窗口的长度即\(k\)有关,于是只考虑一个窗口的贡献最后乘上\(n-k+1\)

\(sir\) 版代码:

for (int i = 1; i <= k; ++i) {
    for (int j = 1; j <= m; ++j) {
        ans = (ans + w[j] * Qpow(j, i - 1) % mod * Qpow(j - 1, k - i) % mod) % mod;
    }
}
ans = ans * Qpow(Qpow(m, k), mod - 2) % mod * (n - k + 1) % mod;

更简洁一点:

for (int i = 1; i <= m; ++i) {
    ans = (ans + (Qpow(i, k) - Qpow(i - 1, k) + mod) % mod * w[i] % mod);
}
ans = ans * (n - k + 1) % mod * Qpow(Qpow(m, k), mod - 2) % mod;
posted @ 2020-10-19 16:19  zfio  阅读(125)  评论(1编辑  收藏  举报