P3600 随机数生成器

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

题目链接:洛谷3600

正解:概率DP

解题报告:

  官方题解:戳这里

  考虑最朴素的计算式就是:$\sum_{i=1}^{x} i*P(ans=i) $为所求。

  上式又等于$\sum_{i=1}^{x} P(ans>=i)$,这个可以理解成对于$j>=i$的$j$分给了所有的$i$,正好是每个一份,系数就没了。

  因为算ans$>=i$很不方便,那么我们转化成求$1-P(ans<i)$。我们直接枚举这个$i$,接下来要做的就是算$P(ans<i)$,用下面的方法可以做到$O(n)$。

  因为是求得$max$我们转化成求$ans<i$的概率之后显然好处理一些…

  考虑$ans<i$的条件就是每个询问的区间中至少存在一个点$<i$(首先把相互包含的大区间去掉),顺着这个思路做下去,听说可以满分23333

  现在将另一种也好理解的做法:对于每个元素我们考虑它能满足哪些区间的询问,不难发现一定也是一个连续的区间,那么我们把点和区间翻转之后,问题变成了:每个点能覆盖一段区间,且覆盖的概率为$p=\frac{i-1}{x}$,问覆盖全段的概率。

  然后就可以转移了,令$f[i]$表示强制选了第$i$个区间之后,覆盖了第$i$个区间右端点之前的所有位置的期望概率。

  那么$f[i]=p*(\sum_{l[i]-1>=r[j]} f[j]*(1-p)^{i-j-1}+[l[i]=1]*(1-p)^{i-1})$,

  最终答案就是$\sum_{r[i]=m} f[i]*(1-p)^{n-i}$。

  大概理解一下上式,强制选了$i$之后再枚举一个之前的$j$,意思是$[j+1,i-1]$这一段都强制不选,其余的类似。

   然后上面这个式子$j$指针单调扫一下,就变成线性的了,话说,如果用树状数组维护多带个$log$也是资瓷的。

//It is made by ljh2000
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <complex>
#include <vector>
#include <cstdio>
#include <string>
#include <bitset>
#include <queue>
#include <cmath>
#include <ctime>
#include <map>
#include <set>
#define lc root<<1
#define rc root<<1|1
#define pr pair<int,int>
#define MP make_pair
#define fr first
#define sc second
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define reg(i,x) for(int i=first[x];i;i=next[i])
using namespace std;
typedef long long LL;
typedef long double LB;
typedef complex<double> C;
const double pi = acos(-1);
const double eps = 1e-9;
const int mod = 666623333;
const int MAXN = 2011;
int n,X,q,stack[MAXN],top,L[MAXN],R[MAXN];
LL f[MAXN],ans,invp[MAXN];
struct seq{ int l,r; }a[MAXN];
inline bool cmp(seq q,seq qq){ if(q.l==qq.l) return q.r>qq.r; return q.l<qq.l; }
inline LL fast_pow(LL x,LL y){ LL r=1; while(y>0) { if(y&1) r*=x,r%=mod; x*=x; x%=mod; y>>=1; } return r; }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
    if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
}

inline void work(){
	n=getint(); X=getint(); q=getint(); for(int i=1;i<=q;i++) { a[i].l=getint(); a[i].r=getint(); }
	sort(a+1,a+q+1,cmp); for(int i=1;i<=q;i++) { while(a[i].r<=a[stack[top]].r) top--; stack[++top]=i; }//去掉包含区间的大区间
	q=top; for(int i=1;i<=q;i++) a[i]=a[stack[i]];

	int head=1,tail=0;
	for(int i=1;i<=n;i++) {
		while(tail<q && a[tail+1].l<=i) tail++;
		while(head<=tail && a[head].r<i) head++;
		L[i]=head; R[i]=tail;
	}

	for(int x=1;x<=X;x++) {//枚举x,计算 P(ans>=x) = 1-P(ans<x)
		LL sum,p,np,fp,leip; int j=0;
		sum=1;//[l[i]==1]
		p=(x-1+mod)%mod*fast_pow(X,mod-2); p%=mod;/*!!!!*/
	   	//p=(x-1)/X

		np=(1-p+mod)%mod;//1-p
		fp=fast_pow(np,mod-2);//1/(1-p)^1
		invp[0]=1; f[0]=1; leip=1;
		for(int i=1;i<=n;i++) invp[i]=1LL*invp[i-1]*fp%mod;
		for(int i=1;i<=n;i++) {
			while(j<i/*!!!*/ && R[j]<L[i]-1) sum-=1LL*f[j]*invp[j]%mod,sum+=mod,sum%=mod,j++;
			f[i]=sum*leip%mod*p%mod; leip=leip*np%mod;
			sum+=1LL*f[i]*invp[i]; sum%=mod;
		}
		LL tot=0; leip=1;
		for(int i=n;i>=1;i--,leip=1LL*leip*np%mod) if(R[i]==q) tot+=f[i]*leip%mod,tot%=mod; else break;
		ans+=(1-tot+mod)%mod; ans%=mod;
	}
	printf("%lld",ans);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("3600.in","r",stdin);
	freopen("3600.out","w",stdout);
#endif
    work();
    return 0;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

  

时间: 2024-10-08 05:05:54

P3600 随机数生成器的相关文章

【矩阵乘】【NOI 2012】【cogs963】随机数生成器

963. [NOI2012] 随机数生成器 ★★ 输入文件:randoma.in 输出文件:randoma.out 简单对照 时间限制:1 s 内存限制:128 MB **[问题描写叙述] 栋栋近期迷上了随机算法,而随机数是生成随机算法的基础.栋栋准备使用线性同余法(Linear Congruential Method)来生成一个随机数列.这样的方法须要设置四个非负整数參数m,a,c,X[0],依照以下的公式生成出一系列随机数{Xn}: X[n+1]=(aX[n]+c) mod m 当中mod

BZOJ 2875: [Noi2012]随机数生成器( 矩阵快速幂 )

矩阵快速幂...+快速乘就OK了 -------------------------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; typedef long long ll; ll MOD, a, c, x, n, g; ll MUL(ll a, ll b) { ll ans = 0; for(; b; b >>= 1

C语言下泊松分布以及指数分布随机数生成器实现

最近实验室的项目需要实现模拟文件访问序列,要求单位时间内的数据请求次数符合泊松分布,而两次请求见的时间间隔符合指数分布.没办法只好重新捡起已经丢掉多时的概率知识.于是也就有了这篇关于在C语言下符合泊松分布和指数分布的随机数生成器的实现. 泊松分布 在实际的事例中,当某一事件,比如进站乘客数量,电话交换机接收到的通话请求以固定的瞬时速率λ独立且随机地出现时,就可以认为该事件在单位时间内发生的次数符合泊松分布. 首先必须由二项分布引出: 如果做一件事情成功的概率是 p 的话,那么独立尝试做这件事情

浅谈随机数生成器及其应用

[导读] 相信来看的读者一定知道在stdlib.h中的rand(),开始觉得它是一个很神奇的东西,绞尽脑汁都想不出它是如何做到的,于是查了下资料知道了如下几点 Windows随机函数产生的随机数不是真正意义下的随机数,而是通过一个随机数种子,然后一些公式,不断地得到下一个"随机数",然后将种子改成这个随机数,然后再不断这样继续. 通过单纯的编程很难得到一个真正的随机函数,UNIX系统下一个随机函数是通过硬件的信息(例如硬件发出的噪音)等等来得到的随机数. 所以经常会出现这样的情况 然后

【bzoj3122】: [Sdoi2013]随机数生成器 数论-BSGS

[bzoj3122]: [Sdoi2013]随机数生成器 当a>=2 化简得 然后 BSGS 求解 其他的特判 : 当 x=t  n=1 当 a=1  当 a=0 判断b==t 1 /* http://www.cnblogs.com/karl07/ */ 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <cmath> 6 #include <map&

Linux的两种随机数生成器

Linux下有两个特殊设备文件/dev/random和/de/urandom,用于生成随机数. /dev/random生成的随机数与当前使用的计算机硬件状态相关,提高了安全性,非常适合对随机数质量要求很高的场景.但是,如果硬件状态改变不足以提供足够多的信息给这个随机数生成器,读它产生的随机数的程序就会block住,一直等到有足够多的硬件状态改变导致数据生成为止. /dev/urandom又称为"无限"或者"非堵塞"("unlimited"[5]/

【BZOJ-3122】随机数生成器 BSGS

3122: [Sdoi2013]随机数生成器 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1362  Solved: 531[Submit][Status][Discuss] Description Input 输入含有多组数据,第一行一个正整数T,表示这个测试点内的数据组数. 接下来T行,每行有五个整数p,a,b,X1,t,表示一组数据.保证X1和t都是合法的页码. 注意:P一定为质数 Output 共T行,每行一个整数表示他最早读到第t页是

Java中的随机数生成器:Random,ThreadLocalRandom,SecureRandom(转)

文中的 Random即:java.util.Random,ThreadLocalRandom 即:java.util.concurrent.ThreadLocalRandomSecureRandom即:java.security.SecureRandom Q:Random是不是线程安全的?A:Random是线程安全的,但是多线程下可能性能比较低.参考:http://docs.oracle.com/javase/7/docs/api/java/util/Random.htmlhttp://stac

Linux下对拍脚本与随机数生成器

对拍脚本 新建一个文档 check.sh 作为对拍脚本. #!/bin/bash while(true)do #死循环 ./data > 1.in #运行数据生成器,将数据输出到1.in ./std < 1.in > out1 #std是标准(暴力)程序 ./now < 1.in > out2 #now是现在要被测的程序 if diff -w out1 out2; then #比较,-w 是忽略行末空格 echo AC #如果一样就输出AC else echo WA cat