【DP】【P5615】 [MtOI2019] 时间跳跃

Description

给定 n 条边,第 i 条边的长度为 i,每条边都有 50% 的概率被选择,求如果选出的边能组成一个平面凸多边形,则方案的权值是方案中边的数量,否则权值为 0。求权值的期望对大质数取模的值。

T 组数据。

Limitations

1nT5000

Solution

因为 disangan233yLOI 的时候用倒数第二档子任务的做法过了C题,所以我也要在 MtOI 用倒数第二档子任务的做法过掉他的 C题

注意到因为所有情况的概率是相同的,因此只需要求出所有情况的总边数,除以 2n 即为期望。

考虑 DP。

根据平面几何的某定理,对于 n 条线段,它们能组成一个平面凸多边形的充要条件是任意 (n1) 条线段的长度之和大于剩下一条线段的长度。

证明上必要性可以通过两点之间线段最短的公理证得,充分性可以对选择的线段数进行数学归纳。

得到推论:对于本题,选出的边能组成平面凸多边形的充要条件是最长的边的长度小于其他边长度之和。

证明上,考虑将最长的边换成其它的边,该边长度变小,剩余边长度之和变大,不等号方向不会改变。

fi 是所有边的长度都不超过 i 时的所有能构成凸多边形情况的边数和。

考虑这些情况一共分两类,第一类是不包括长度为 i 的边的情况,第二类是包括长度为 i 的边的情况。

对于第一类情况,边数和就是 fi1

对于第二类情况,考虑剩下的边长度之和只要大于 i 即可。

gj 是选至少两条边且边的长度和为 j 时(不要求构成凸多边形)所有情况的边数和,hj 是选至少两条边且边的长度和为 j 时(不要求构成凸多边形)的总情况数。这两个值均要求所选的边的长度不超过 i,实际上是省略了这两个值的第一维。

因此有

fi=fi1+j=i+1i2(gj+hj)

这里加 hj 是因为对于每种情况都可以加一条长度为 i 的边来构成一个凸多边形,对于所有情况,每种情况可以加一条边,一共可以加 hj 条边,而 gj 是这种情况原有的边数。

考虑递推 gh

考虑从小到大枚举 i,即边长上限增加时,gh 的变化。

对于 gj,所有任选两条边且边权和为 (ji) 的情况,都可以加入这条边来达到边权和为 j,同时还有不选择加入长度为 i 的边这种情况,因此有

gj=gj+gji+hji     (j>i)

当然,对于选 i 和另一条边的情况,也能对 g 产生贡献,因此有

gj+i=gj+i+2      (j<i)

h 的递推同理:

hj=hj+hji     (j>i)

hj+i=hj+i+1     (j<i)

然后用得到的 gh 递推 f 即可。

考虑复杂度:所有边之和是 O(n2) 级别的,因此每次更新 gh 都是 O(n2) 的,一共更新 O(n) 次,因此更新 gh 的总复杂度 O(n3)。而递推 f 时每次也是 O(n2) 的,一共更新 O(n) 次,所以复杂度也是 O(n3)。因此总时间复杂度 O(n3),可以通过前 4 个子任务。

但是注意到我们的空间复杂度是 O(n2) 的,因此对于第 5 个子任务,我们的空间完全能够承受,同时时间复杂度仍然是多项式级,因此 本地挂机打表 即可通过子任务 5,表的长度是 48k,甚至不需要用字符加密来缩短代码长度,直接存明文即可。

Code

#include <cstdio>
#include <algorithm>

const int maxn = 100005;
const int MOD = 1000000007;
const int MODP = 1000000005;

int T, N;
int query[maxn];
ll frog[maxn], gorf[maxn], h[maxn];

ll mpow(const ll x, int y);

int main() {
  freopen("1.in", "r", stdin);
  qr(T);
  for (int i = 1; i <= T; ++i) {
    qr(query[i]);
    N = std::max(N, query[i]);
  }
  gorf[3] = 2; h[3] = 1;
  for (int i = 3, upceil = 3; i <= N; ++i) {
    for (int j = i + 1; j <= upceil; ++j) {
      (frog[i] += gorf[j] + h[j]) %= MOD;
    }
    (frog[i] += frog[i - 1]) %= MOD;
    upceil += i;
    for (int j = upceil; j >= i; --j) {
      (gorf[j] += gorf[j - i] + h[j - i]) %= MOD;
      (h[j] += h[j - i]) %= MOD;
    }
    for (int j = 1; j < i; ++j) {
      (gorf[j + i] += 2) %= MOD;
      (h[j + i] += 1) %= MOD;
    }
  }
  for (int i = 1; i <= T; ++i) {
    qw(frog[query[i]] * (mpow(mpow(2, query[i]), MODP)) % MOD, '\n', true);
  }
  return 0;
}

ll mpow(const ll x, int y) {
  ll _ret = 1, _tmp = x;
  while (y) {
    if (y & 1) (_ret *= _tmp) %= MOD;
    (_tmp *= _tmp) %= MOD;
    y >>= 1;
  }
  return _ret;
}
posted @   一扶苏一  阅读(249)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2018-11-03 【数学】【CF27E】 Number With The Given Amount Of Divisors
2018-11-03 【单调队列】【P3957】 跳房子
点击右上角即可分享
微信分享提示