Groundhog and Gaming Time
题目描述;
在
P
K
U
W
C
2019
d
a
y
2
PKUWC2019day2
PKUWC2019day2晚上,
n
{n}
n个同学包括
S
o
e
t
d
i
t
,
T
X
1145967673
,
Z
P
A
Y
A
U
R
,
G
r
o
u
n
d
h
o
g
Soetdit,TX1145967673,ZPAYAUR,Groundhog
Soetdit,TX1145967673,ZPAYAUR,Groundhog做出了一个了不起的决定:他们准备参加一个车队,他们将分担一夜直到黎明。
那天晚上,他们一个接一个地上网。第
[
i
]
[i]
[i]个同学将从
[
L
i
,
R
i
]
[L_i,R_i]
[Li,Ri]上线。但是有人报告了一条消息:他们的老师晚上会来。每个人都有
1
2
\frac{1} {2}
21的可能性来接收消息,而接收到该消息的人将不会在晚上上网。他们的可用游戏时间是所有游戏者都在线时。因此,他们开始思考一个问题:对他们的可用时间模的平方的期望是什么
998244353
{998244353}
998244353
简化的问题:有
n
{n}
n个段
[
L
i
,
R
i
]
[L_i,R_i]
[Li,Ri],每个段都有可能选择
1
2
\frac {1} {2}
21,问题是计算
∣
[
L
a
1
,
R
a
1
]
∩
[
L
a
2
,
R
a
2
]
∩
⋯
∩
[
L
a
m
,
R
a
m
]
∣
2
| [L_ {a_1},R_ {a_1}] \cap [L_ {a_2},R_ {a_2}] \cap \cdots \cap [L_ {a_m},R_ {a_m}] | ^ 2
∣[La1,Ra1]∩[La2,Ra2]∩⋯∩[Lam,Ram]∣2
m
o
d
mod
mod
998244353
{998244353}
998244353的期望值。
a
i
a_i
ai表示选定的第
i
t
h
{i ^ {th}}
ith个段的数量。
输入描述:
第一行包含一个整数
n
{n}
n,表示学生人数。
接下来的
n
{n}
n行分别包含两个整数
L
i
,
R
i
L_i,R_i
Li,Ri,这表示第
i
i
i个学生的游戏时间。
输出描述:
输出一行包含答案模 998244353 {998244353} 998244353。
样例输入:
6
2 2
1 2
1 4
1 5
3 5
3 6
样例输出:
405536771
思路:
求期望的方法有很多种,这题我们选择期望dp求期望的方法。
首先由于数据较大我们需要先对数据进行离散化,然后我们按左右端点升序排序并考虑每一段对答案的贡献。
我们设区间A出现在b条线段中,则有:
P
(
A
)
=
2
a
−
1
2
n
P(A)=\frac{2^a-1}{2^n}
P(A)=2n2a−1
应为不能全选,所以要减一。
所以:
E
(
A
)
=
2
a
−
1
2
n
∗
∣
A
∣
(
E(A)=\frac{2^a-1}{2^n}*|A|(
E(A)=2n2a−1∗∣A∣(听说两个‘|’还可以表示size
)
)
)
那么在所有
n
n
n条线段中
P
i
P_i
Pi的贡献
(
a
i
(a_i
(ai条线段包含
A
)
A)
A):
∑
E
(
Q
)
∗
∣
P
i
∣
\sum E(Q)*|P_i|
∑E(Q)∗∣Pi∣
=
∑
j
(
2
b
j
−
1
2
n
∗
∣
Q
j
∣
)
∗
∣
P
i
∣
=\sum_j(\frac{2^{b_j}-1}{2^n}*|Q_j|)*|P_i|
=∑j(2n2bj−1∗∣Qj∣)∗∣Pi∣
=
∑
j
(
2
b
j
2
n
∗
∣
Q
j
∣
)
∗
∣
P
i
∣
−
∣
P
i
∣
∗
m
x
=\sum_j(\frac{2^{b_j}}{2^n}*|Q_j|)*|P_i|-|P_i|*mx
=∑j(2n2bj∗∣Qj∣)∗∣Pi∣−∣Pi∣∗mx
我们可以用线段树来维护
2
b
i
2^{b_i}
2bi,最后除以
2
n
2^n
2n并减去
m
x
mx
mx即可
具体细节请看代码。
A C AC AC C o d e Code Code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 998244353;
const int MAXN = 1e6+5;
const int N = 1e9 + 1;
ll a[MAXN << 2], b[MAXN << 2], trl[MAXN], trr[MAXN], l[MAXN];
ll r[MAXN], lsh[MAXN], cnt, ans;
int n;
ll ksm (ll a, ll b) {
ll ret = 1;
while (b){
if(b & 1) ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}//快速幂求逆元
bool cmp1 (int x, int y){ return l[x] < l[y]; }
bool cmp2 (int x, int y){ return r[x] < r[y]; }
void build_tree (int pos, int l, int r){//建线段树
a[pos] = lsh[r + 1] - lsh[l];
b[pos] = 1;
if (l == r) return;
int mid = l + r >> 1;
build_tree (pos << 1, l, mid);
build_tree (pos << 1 | 1, mid + 1, r);
}
void f (int pos, int l, int r, ll num, int x, int y){//将区间[l,r]的权值都*num
if(l <= x && r >= y){
a[pos] = a[pos] * num % mod;
b[pos] = b[pos] * num % mod;
return;
}
int mid = x + y >> 1;
if (l <= mid) f (pos << 1, l, r, num, x, mid);
if (r > mid) f (pos <<1 | 1, l, r, num, mid + 1, y);
a[pos] = (a[pos << 1] + a[pos << 1 | 1]) * b[pos] % mod;//b[pos]就是lazy标记,这里可以不用下推标记。
}
int main (){
lsh[1] = 0;
lsh[2] = N;//添加最左最优的端点,用于处理所有线段都不选择的情况
cnt = 2;
scanf ("%d", &n);
for (int i = 1; i <= n; ++i){
scanf("%lld%lld", l + i ,r + i);
lsh[++cnt] = l[i];
lsh[++cnt] = ++r[i];
}
sort (lsh + 1, lsh + cnt + 1);
cnt = unique(lsh + 1, lsh + cnt + 1) - lsh - 1;//离散化
build_tree(1, 1, cnt - 1);//建线段树,i点的初始权值为区间[i,i+1]的长度
for (int i = 1; i <= n; ++i){
l[i] = lower_bound(lsh + 1, lsh + cnt + 1, l[i]) - lsh;//映射到原值
r[i] = lower_bound(lsh + 1, lsh + cnt + 1, r[i]) - lsh;
}
for (int i = 1; i <= n; ++i) trl[i] = trr[i] = i;
sort (trl + 1, trl + n + 1, cmp1);//线段按左端点排序
sort (trr + 1, trr + n + 1, cmp2);//线段按右端点排序
int t1 = 1, t2 = 1;
for (int i = 1; i < cnt; ++i){//遍历区间[i,i+1],维护包含该区间的线段的交集的期望。这个期望通过计算所有区间的贡献和得到
//从m的线段,找出a个线段包含区间[i,i+1]。计算这a个线段的交集的期望,等于每个期间的贡献总和。
//a个线段,有b个线段覆盖区间[j,j+1],则区间[j,j+1]的贡献=长度*(2^b-1)/2^n。其中-1和/2^n最后再去除
for (; t1 <= n && l[trl[t1]] <= i; ++t1)//添加左端点小于等于i的线段的贡献
f(1, l[trl[t1]], r[trl[t1]] - 1, 2, 1, cnt - 1);//添加贡献,等于该区间的权值*2
for (;t2 <= n && r[trr[t2]] <= i; ++t2)//删除右端点小于等于i的线段的贡献
f(1, l[trr[t2]], r[trr[t2]] - 1, ksm(2, mod - 2), 1, cnt - 1);//添加贡献,等于该区间的权值-2
ans = (ans + a[1] * (lsh[i + 1] - lsh[i])) % mod;//计入答案,此时ans=sum(方案权值*方案数)
}//注意此时统计完时,未减去考虑所有线段都不选择的情况,即贡献=长度*(2^b-1)/2^n中的-1
ans = (ans + mod - 1ll * N * N % mod) % mod;//减去所有线段都不选择的情况
for (int i = 1; i <= n; ++i)
ans = ans * ksm(2, mod - 2) % mod;//除2^n,得到期望
printf("%lld\n", ans);
}
这篇博客介绍了Groundhog和朋友们在PKUWC2019day2晚上的游戏时间问题。他们面对老师可能来访的情况,计算了在所有同学都在线时可用游戏时间的期望值模998244353的平方。博主使用期望DP和线段树的方法解决这个问题,通过离散化和排序处理数据,并通过线段树维护信息,最终计算出期望值的贡献。

被折叠的 条评论
为什么被折叠?



