Luogu3600 随机数生成器

题面

传送门

Sol

sto   s t o     fdf f d f
sto   s t o     fateice f a t e i c e

显然,如果一个区间包含了另一个区间,那么它的最小值不会有贡献,直接去掉

考虑枚举最大值 k k
求出所有区间满足最小值小于等于k的概率,设为 P[k] P [ k ]
那么 k k 的贡献就是(P[k]P[k1])k,相当于差分了一下

然后考虑算出 P[k] P [ k ]
f[i] f [ i ] 表示到 i i 时,所有右端点都小于等于i的区间都满足要求的概率
枚举右端点在 i i 的所有左端点j
枚举在哪里弄一个小于等于 k k

f[i]=l=jif[l1](kx)(1kx)il

枚举的这个点之前的随便选,而后面的只能选大于 k k
这样才能做到不重复计算,因为如果后面的也随便选,那么会和之前的有交集
然后这是n3x的,居然过了
n,x<=2000 n , x <= 2000

把上面的转移式拆一下,把 f[l1](1kx)l f [ l − 1 ] ∗ ( 1 − k x ) − l 做个前缀和就好了
特别注意如果 i=l i = l 并且 k=x k = x 时, (1kx)il ( 1 − k x ) i − l 要当成 1 1
那么特判一下就好了

fdf   orz     o r z
fateice f a t e i c e   orz     o r z

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(2005);
const int Zsy(666623333);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, x, Q, tp, f[_], g[_], ans, first[_], nxt[_], inv;
struct Query{
    int l, r;

    IL int operator <(RG Query B) const{
        return l != B.l ? l < B.l : r < B.r;
    }
} tmp[_], q[_];

IL int Pow(RG ll x, RG ll y){
    RG ll ret = 1;
    for(; y; y >>= 1, x = x * x % Zsy)
        if(y & 1) ret = ret * x % Zsy;
    return ret;
}

IL void Upd(RG int &x, RG int y){
    x += y;
    if(x >= Zsy) x -= Zsy;
}

IL int Sum(RG int l, RG int r){
    return (g[r] - (l < 0 ? 0 : g[l]) + Zsy) % Zsy;
}

IL int Calc(RG int v){
    RG int p1 = 1LL * inv * v % Zsy, p2 = (1 + Zsy - p1) % Zsy, p3 = Pow(p2, Zsy - 2);
    Fill(g, 0), Fill(f, 0), g[0] = p3, f[0] = 1;
    for(RG int i = 1; i <= n; ++i){
        for(RG int p = first[i]; p; p = nxt[p]){
            if(i > q[p].l) Upd(f[i], 1LL * Sum(q[p].l - 2, i - 2) * p1 % Zsy * Pow(p2, i) % Zsy);
            Upd(f[i], 1LL * f[i - 1] * p1 % Zsy);
        }
        if(!first[i]) f[i] = f[i - 1];
        g[i] = g[i - 1], Upd(g[i], 1LL * f[i] * Pow(p3, i + 1) % Zsy);
    }
    return f[n];
}

int main(RG int argc, RG char *argv[]){
    n = Input(), x = Input(), tp = Input(), inv = Pow(x, Zsy - 2);
    for(RG int i = 1; i <= tp; ++i) tmp[i] = (Query){Input(), Input()};
    sort(tmp + 1, tmp + tp + 1);
    for(RG int i = 1; i <= tp; ++i){
        while(q[Q].r >= tmp[i].r) --Q;
        if(tmp[i].l > q[Q].l) q[++Q] = tmp[i];
    }
    for(RG int i = 1; i <= Q; ++i) nxt[i] = first[q[i].r], first[q[i].r] = i;
    for(RG int i = 1, lst = 0; i <= x; ++i){
        RG int now = Calc(i);
        ans = (ans + 1LL * i * ((now - lst + Zsy) % Zsy) % Zsy) % Zsy;
        lst = now;
    }
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值