【HDU 6410】序列期望 【期望 + 数学】

Problem Description
“看似随机,实则早已注定”——光羽

度度熊有n个随机变量 x1,x2,...,xn x 1 , x 2 , . . . , x n 。给定区间 [l1,r1],...,[ln,rn] [ l 1 , r 1 ] , . . . , [ l n , r n ] ,变量 xi x i 的值会等概率成为区间 [li,ri] [ l i , r i ] 中的任意一个整数

显然这n个随机变量的值会有一共 ni=1(rili+1) ∏ i = 1 n ( r i − l i + 1 ) 种情况,且每种情况出现的概率为 ni=11rili+1 ∏ i = 1 n 1 r i − l i + 1

对于某种情况,令 h=maxx1,x2,...,xn h = m a x x 1 , x 2 , . . . , x n ,定义这种情况的权值为: ni=1(hxi+1) ∏ i = 1 n ( h − x i + 1 ) .

度度熊想知道权值的期望是多少?请将答案对109+7取模后输出。

PS:不清楚期望是啥?为什么不问问神奇的百度呢?

Input
第一行一个数,表示数据组数T。

每组数据第一行一个整数n;接下来n行,每行两个数,表示li和ri。

数据组数T=100,满足:

1n100 − 1 ≤ n ≤ 100
1liri104 − 1 ≤ l i ≤ r i ≤ 10 4

其中70%的数据满足ri≤100。

Output
每组数据输出一行,每行仅包含一个数,表示期望。

假设答案为pq,请输出p×q−1 mod 109+7,此处q−1为q的逆元。

Sample Input
2
3
2 5
2 4
2 5
3
1 1
2 3
1 1

Sample Output
875000012
500000010

Hint

第二组数据的解释:序列只有两种情况(1,2,1)和(1,3,1),权值分别为2*1*2=4和3*1*3=9,答案为(4+9)/2,在模域下为500000010。
分析: 因为是要求期望,所以是每一种情况概率乘相对应的权值,求和就行了。然后我们可以发现每一种情况的概率都是一样的,所以我们只需要算出所有情况的总权值之和就行了。但是如果按照题意给的公式求解,时间肯定爆炸,所以我们要优化这个公式,我们可以枚举h,对于同一个h来说 (lihrihxi=lihxi+1)(ri<hrixi=lihxi+1) ( ∏ l i ≤ h ≤ r i ∑ x i = l i h h − x i + 1 ) ( ∏ r i < h ∑ x i = l i r i h − x i + 1 ) 这个公式可以计算出包含最大值为h的所有情况,但是我们想要的是最大值为h的总权值,所以我们可以减掉小于h的那部分就可以了。
优化后的公式还是蛮不好想的,所以我抽象出来了一个简单的问题,把这个搞懂了,应该就可以理解上面的公式:
3 种物体,每种物体有3个(每个物体都有自己独特的权值),你每次必须要从一种物体里面挑选一个, 这样会有3*3*3种选择,每种选择的权值定义为:所选的三个物体的权值之和.。现在求所有选择的总权值之和。
a3b3c3 a 3 b 3 c 3
a2b2c2 a 2 b 2 c 2
a1b1c1 a 1 b 1 c 1
最暴力的一种就是3*3*3的复杂度求解。
另外一种呢,所有情况的总权值之和为: (a1+a2+a3)(b1+b2+b3)(c1+c2+c3) ( a 1 + a 2 + a 3 ) ∗ ( b 1 + b 2 + b 3 ) ∗ ( c 1 + c 2 + c 3 ) ,为什么是这个公式呢? 应该很容易理解吧,仔细想想?

代码

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <list>
#include <string>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define rep(i, l, r) for(int i = l; i < r; i++)
#define per(i, r, l) for(int i = r; i >= l; i--)

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;

const int N = (int) 1e5 + 11;
const int M = (int) 1e6 + 11;
const int MOD = (int) 1e9 + 7;
const double EPS = (double) 1e-9;
const double PI = (double)acos(-1.0);
const int INF = (int) 0x3f3f3f3f;
const ll INFF = (ll) 0x3f3f3f3f3f3f3f3f;

void read(int &x){
    char ch = getchar(); x = 0;
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
}
/*-----------------------------------------------------------------------------------*/ 

int l[N], r[N];
int Pow(int a, int b, int c = MOD){
    int s = 1; a %= c;
    while(b){
        if(b & 1) s = s * 1ll * a % c;
        b >>= 1;
        a = a * 1ll * a % c;
    }
    return s;
}
int inv2 = Pow(2, MOD - 2);
int GetSum(int l, int r){ // 公差为1,首项为l尾项是r的 等差数列的和
    if(l > r) return 0;
    return (l + r) * 1ll * (r - l + 1) % MOD * inv2 % MOD; 
}
int main(){
    #ifndef ONLINE_JUDGE
        freopen("in.txt", "r", stdin);
        freopen("out.txt", "w", stdout);
    #endif
    int T; scanf("%d", &T);
    while(T--){
        int n; scanf("%d", &n);
        int mxL = 0, mxR = 0, prod = 1;
        rep(i, 0, n) {
            scanf("%d%d", l + i, r + i);
            mxL = max(mxL, l[i]); mxR = max(mxR, r[i]); // 找到枚举h的下界和上界
            prod = prod * 1ll * (r[i] - l[i] + 1) % MOD; // 需要求分母的逆元
        } 
        //cout << mxL << " " << mxR << " " << prod <<" \n"; 
        int ans = 0;
        rep(h, mxL, mxR + 1){
            int sum1 = 1, sum2 = 1; 
            rep(i, 0, n){
                int le = l[i]; int ri = min(r[i], h); 
                sum1 = sum1 * 1ll * GetSum(h - ri + 1, h - le + 1) % MOD;
                ri = min(r[i], h - 1);
                sum2 = sum2 * 1ll * GetSum(h - ri + 1, h - le + 1) % MOD;
                // cout << sum1 << " " << sum2 <<"\n";
            }
            if(h > mxL) sum1 = (sum1 - sum2 + MOD) % MOD; // 第一项不用减
            ans += sum1; if(ans >= MOD) ans -= MOD;
        }
        //cout << ans << " " << Pow(prod, MOD - 2) << " " <<"\n";
        ans = ans * 1ll * Pow(prod, MOD - 2) % MOD;
        printf("%d\n", ans); 
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值