洛谷 P3600 隨機數生成器(期望 dp)

2022-06-29 19:45:12 字數 4878 閱讀 2784

題面傳送門

我竟然獨立搞出了這道黑題!incredible!

u1s1 這題是我做題時間跨度最大的題之一……

首先講下我四個月前想出來的 \(n^2\log n\) 的做法吧。

記 \(f(a)=\max\limits_^q\min\limits_^a_j=x\)

首先期望轉概率,設 \(p_i\) 表示 \(f(a)=x\) 的概率,答案即為 \(\sum p_i\times i\)。

注意到這題直接求 \(f(a)=x\) 的概率不是特別容易,故考慮換個思路,求出 \(f(a)\geq x\) 的概率 \(t_x\) 然後求個差分即可。是我們的任務轉化為求 \(t_x\) 的值。

記序列中 \(的數為 \(0\),\(\ge x\) 的數為 \(1\),那麼原題等價於這樣乙個問題,有乙個長度為 \(n\) 的隨機 \(01\) 序列,每一位生成 \(0\) 的概率為 \(p_0=\dfrac\),生成 \(1\) 的概率為 \(p_1=\dfrac\),給定 \(q\) 個區間 \([l_i,r_i]\),問有多大概率滿足這 \(q\) 個區間中至少存在乙個區間中全是 \(1\)

這個問題可以用 \(dp\) 求解,設 \(dp_\) 表示當前填好了前 \(i\) 位,上乙個 \(0\) 的位置為 \(j\),並且到現在為止不存在全為 \(1\) 的區間的概率。

考慮轉移,記 \(mx_r\) 表示 \(\max\limits_^ql_i(r_i=r)\),分三種情況轉移:

**:

#include using namespace std;

#define fi first

#define se second

#define fz(i,a,b) for(int i=a;i<=b;i++)

#define fd(i,a,b) for(int i=a;i>=b;i--)

#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)

#define fill0(a) memset(a,0,sizeof(a))

#define fill1(a) memset(a,-1,sizeof(a))

#define fillbig(a) memset(a,63,sizeof(a))

#define pb push_back

#define ppb pop_back

#define mp make_pair

typedef pairpii;

typedef long long ll;

const int maxn=2e3+5;

const int mod=666623333;

int n,m,q,l[maxn+5],r[maxn+5],inv,p[maxn+5],ans[maxn+5];

int qpow(int x,int e)

int add(int x,int y)

struct node s[maxn*4+5];

void build(int k,int l,int r)

void pushdown(int k)

if(s[k].lz^1)

}void modify(int k,int l,int r,int x) pushdown(k);int mid=(s[k].l+s[k].r)>>1;

if(r<=mid) modify(k<<1,l,r,x);

else if(l>mid) modify(k<<1|1,l,r,x);

else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);

s[k].val=add(s[k<<1].val,s[k<<1|1].val);

}void update(int k,int ind,int x)

pushdown(k);int mid=(s[k].l+s[k].r)>>1;

if(ind<=mid) update(k<<1,ind,x);

else update(k<<1|1,ind,x);

s[k].val=add(s[k<<1].val,s[k<<1|1].val);

}int query(int k,int l,int r)

void solve(int x)

}int main()

/*4 3 2

1 32 4

*/

然鵝當我寫好了**,有十足的信心切掉這道題的時候……

…………tle 70?

當時還以為是自己常數寫大了,花了九牛二虎之力卡常卻無果,xtbz

於是這道題就光榮地在我【嘗試過的題】中躺了四個月。

這週三的時候又重新翻到了這個題,順帶著把 \(n^2\) 的演算法想出來了。

於是現在我才知道當時平方對數的演算法過不了是出題人故意卡的

注意到在之前平方對數的演算法中,我們對 \(t_x\) 的定義為 \(f(a)\geq x\) 的概率,如果換成 \(f(a)\le x\) 的概率會有什麼區別呢?

這次,我們記序列中 \(\le x\) 的數為 \(1\),\(>x\) 的數為 \(0\),那麼求解 \(f(a)\le x\) 的部分等價於如下的問題:有乙個長度為 \(n\) 的隨機 \(01\) 序列,每一位生成 \(0\) 的概率為 \(p_0=1-\dfrac\),生成 \(1\) 的概率為 \(p_1=\dfrac\),給定 \(q\) 個區間 \([l_i,r_i]\),問有多大概率滿足這 \(q\) 個區間中每乙個區間中都存在至少乙個 \(1\)

還是考慮 \(dp\),記 \(dp_i\) 為當前考慮到第 \(i\) 位,且第 \(i\) 位是 \(1\) 的概率。

考慮轉移,我們列舉上乙個 \(1\) 的位置 \(j\),就有 \(dp_i=\sum\limits_^dp_j\times p_0^\times p_1\)。

但事實上這個轉移方程是錯誤的,因為並不是所有的 \(j\) 都可以轉移,比方說 \(i=4,j=1,l_1=2,r_1=3\),那麼 \([l_1,r_1]\) 中所有數都是 \(0\),不符合要求。

那麼問題就來了,究竟什麼樣的 \(j\) 才能轉移到 \(i\) 呢?

顯然 \(j\) 可以轉移到 \(i\) 當且僅當不存在 \(i\in[1,q]\) 使得 \([l_i,r_i]\subseteq(j,i)\),並且我們還可以發現一件事,那就是如果 \(j\) 可以轉移到 \(i\),並且 \(j,那麼 \(j+1\) 也一定可以,故合法的決策點是乙個右端點為 \(i-1\) 的區間,記 \(i\) 對應決策點區間的左端點為 \(l_i\),那麼上述方程式可改寫為 \(dp_i=\sum\limits_^dp_j\times p_0^\times p_1\)。

那麼怎麼求出 \(l_i\) 呢?記 \(mx_r\) 表示 \(\max\limits_^ql_i(r_i=r)\)。那麼我們只需檢驗是否存在某個 \(k\in(j,i)\) 使得 \(mx_k>j\) 即可,我們還可以注意到 \(l_i>l_\),也就是 \(l_i\) 單調遞增。故可用 two pointers+st 表在 \(n\log n\) 預處理、\(\mathcal o(n)\) 求解的時間內求出 \(l_i\),具體來說,st 表維護 \(\max\limits_^rmx_i\),然後檢驗 \(\max\limits_^mx_k>j\) 即可。

求出 \(l_i\) 之後轉移就變得異常容易了。我們可將轉移方程式進一步改寫為 \(dp_i=\sum\limits_^\dfrac\times p_0^\times p_1\),注意到前面 \(\dfrac\) 只與 \(j\) 有關,後面 \(p_0^\times p_1\) 只與 \(i\) 有關,故可以維護 \(\dfrac\) 的字首和,這樣即可實現 \(\mathcal o(1)\) 轉移。

時間複雜度 \(n^2\)。

終於把自爆的心頭之恨化解掉了,爽

#include using namespace std;

#define fi first

#define se second

#define fill0(a) memset(a,0,sizeof(a))

#define fill1(a) memset(a,-1,sizeof(a))

#define fillbig(a) memset(a,63,sizeof(a))

#define pb push_back

#define ppb pop_back

#define mp make_pair

templatevoid chkmin(t1 &x,t2 y)

templatevoid chkmax(t1 &x,t2 y)

inline void putc(char x)

templatevoid read(t &x)

templatevoid recursive_print(t x)

templatevoid print(t x)

void print_final()

}const int maxn=2e3;

const int log_n=11;

const int mod=666623333;

int n,m,q,inv,mx[maxn+5],st[maxn+5][log_n+2],pre[maxn+5];

int pw[maxn+5],ipw[maxn+5],dp[maxn+5],sum[maxn+5],ans[maxn+5];

int qpow(int x,int e)

int query_mx(int l,int r) int res=0;

for(int i=1;ifor(int i=1;i<=m;i++) res=(res+1ll*(ans[i]-ans[i-1]+mod)*i)%mod;

printf("%d\n",res);

return 0;

}

luogu3600 隨機數生成器

給定n個 1 x 的隨機整數 a 1,a 2,a 3,a n 和q個詢問區間 l i,r i 求出 max 的期望 對於10 的資料,n,x,q 6 對於另外20 的資料,q 1 對於50 的資料,n,x,q 300 對於100 的資料,1 n,x,q 2000 對於每個i,1 li ri n 又是...

洛谷P2044 隨機數生成器

棟棟最近迷上了隨機演算法,而隨機數是生成隨機演算法的基礎。棟棟準備使用線性同餘法 linear congruential method 來生成乙個隨機數列,這種方法需要設定四個非負整數引數m,a,c,x 0 按照下面的公式生成出一系列隨機數 x n 1 ax n c mod m 用這種方法生成的序列...

洛谷P3306 隨機數生成器

題意 給你乙個數列,a1 x,ai a ai 1 b p,求第乙個是t的是哪一項,或者永遠不會有t。解 迴圈節不會超過p。我們使用bsgs的思想,預處理從t開始跳 p步的,插入hash表內。然後每次把a1跳 p步,來看是否在hash表中存在。這樣發現我們有40,wa了60分。為什麼呢?考慮是否存在兩...