问题描述
有一条数轴,还有一个区间的集合,集合大小为n。 现在等概率的从集合中选出集合的一个子集,求取出的子集的区间并集的期望长度。 空集的区间并长度被认为是0。
输入描述
输入文件包含多组数据,第一行为数据组数T。 对于每组数据,第一行为集合的大小n。 接下来的n行,每行两个数l , r代表集合内区间的左右端点坐标。 1≤n≤100,000. −1,000,000,000≤l≤r≤1,000,000,000.
输出描述
对于每组数据,输出期望乘2n 对 1000000007(109+7) 取模的结果。
输入样例
2 1 0 1 2 0 2 1 3
输出样例
1 7
Hint
对于第二个例子,期望为40+2+2+3=47。
一个区间贡献值为该区间长度乘以有多少个线段子集覆盖了这个区间。如果一个区间被m个线段覆盖,那么他所贡献的值为区间长度乘以(2^n - 2^(n - m)),一共有2^n个子集,有2^(n - m)个子集没有覆盖到该区间,所以这两个数之差就是包含该区间的子集数。剩下的就是维护每个区间被线段覆盖次数。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int mod = 1e9 + 7; const int maxn = 1e5 + 10; int n, t; long long p[maxn]; struct node { int x, cnt; bool operator< (const node& ano)const { if (x == ano.x) return cnt < ano.cnt; else return x < ano.x; } }seg[maxn * 2]; int main() { p[0] = 1; for (int i = 1; i <= 1e5 + 5; i++) p[i] = (p[i - 1] * 2) % mod; scanf("%d", &t); while(t--) { scanf("%d", &n); int l, r, now = 0; for (int i = 0; i < n; i++) { scanf("%d%d", &l, &r); seg[now].x = l; seg[now++].cnt = 1; seg[now].x = r; seg[now++].cnt = -1; } sort(seg, seg + now); long long ans = 0; int pre = seg[0].x; int num = 0; for (int i = 0; i < now; i++) { ans = (ans + ((seg[i].x - pre) * (p[n] - p[n - num] + mod)) % mod) % mod; pre = seg[i].x; num += seg[i].cnt; } printf("%lld\n", ans); } return 0; }