題解 CF1264C 【Beautiful Mirrors with queries】
阿新 • • 發佈:2021-01-20
-
CF1264C 【Beautiful Mirrors with queries】
一道期望入門好題,有著非常巧妙的解法。
-
Prelude
對於這個題面我們一眼望去會想到線性遞推一個期望dp式子求解。
但是發現無法實現。
原因很明顯,對於斷點的修改和維護,我們並不能每次都重新 \(O(n)\) 做一遍
我又想到用資料結構維護答案,但是其實大材小用了。
其實我們只需要推出一個區間的通項式來 \(O(1)\) 求解即可。
這種思路其實經常存在於一些線段樹的資料結構題中,推出可行的懶惰標記的 \(O(1)\) 下傳公式。
至少我是在 \(\texttt{P6327}\) 和 \(\texttt{P2122}\)
-
Solution
推dp時,我們考慮題面中 \(\text{從編號小於等於當前鏡子的且最近的檢查點開始提問}\) 中不難想到為了防止後效性,我們
應該定義 \(f_i\) 為從 \(i\) 點開始詢問一直到 \(n\) 點的期望值。
起初每次傷心後會從第一個點開始。則有
\[f_i=p_i\times f_{i+1}+(100\%-p_i)\times f_1 \]移項消元得到
\[f_1=\frac{1}{p_n}+\frac{1}{p_n\times p_{n-1}}+\dots+\frac{1}{p_n\times p_{n-1}\times \dots\times p_{1}} \]畢竟 \(f_1\) 就是 \(f(1\rightarrow n)\) ,那麼根據 期望 的線性性質我們可以引入區間 \([l,r]\) ( \(l,r\) 都是復活點), 可以得到
\[f_l-f_r=f(l\rightarrow r)=\frac{1}{p_r}+\frac{1}{p_r\times p_{r-1}}+\dots+\frac{1}{p_r\times\dots\times p_l} \]\[=\frac{\prod_{i\in [l,r-1]}p_i+\prod_{i\in [l,r-2]}p_i+\dots+p_l+1}{\prod_{i=1}^rp_i/\prod_{i=1}^{l-1}p_i} \]那麼現在我們又只需要維護:
- \(\displaystyle m_x=\prod_{i=1}^{x}p_i\) (分母);
- \(\displaystyle s_x=\sum_{i=1}^xm_i\) (分子);
- 和 \(\displaystyle ans=\sum_{l,r\in \texttt{check-points}} f(l\rightarrow r)\);
每次修改的 \(\texttt{check-points}\) 可以用 set
在 \(O(\log n)\) 時間維護。
-
Code
題面有要求求逆,而逆元可以直接用快速冪求解。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
//#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define INL inline
//Tosaka Rin Suki~
using namespace std;
INL void read(ll &x)
{
x=0;ll w=1;
char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')
{x=(x<<1)+(x<<3)+ch-48,ch=getchar();}
x*=w;
}
INL int mx(int a,int b){return a>b?a:b;}
INL int mn(int a,int b){return a<b?a:b;}
const ll MOD=998244353;
const int N=200005;
ll n,q,p;
ll prod[N],sp[N];
set <int> s;
inline ll P(ll x,ll p)
{
ll res=1;
while(p)
{
if(p&1)res=res*x%MOD;
x=x*x%MOD;p>>=1;
}
return res;
}
inline ll calc(ll l,ll r)
{
ll phi=sp[r-1]-sp[l-1]+MOD;
if(phi>=MOD)phi-=MOD;
return (phi*P(prod[l-1],MOD-2)%MOD+1)*P(prod[r]*P(prod[l-1],MOD-2)%MOD,MOD-2)%MOD;
}
int main()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
read(n);read(q);
prod[0]=1;
for(int i=1;i<=n;i++)
{
read(p);p=p*P(100,MOD-2)%MOD;prod[i]=prod[i-1]*p%MOD;
sp[i]=(sp[i-1]+prod[i])%MOD;//維護字首積和字首積的字首和
}
ll ans=(sp[n-1]+1)%MOD*P(prod[n],MOD-2)%MOD;
s.insert(1);s.insert(n+1);
for(int i=1;i<=q;i++)
{
read(p);
set<int>::iterator it=s.lower_bound(p);
if(*it==p)
{
int l=*(--it);
it++;
int r=*(++it);
ans=ans-calc(l,p-1)+MOD;
if(ans>=MOD)ans-=MOD;
ans=ans-calc(p,r-1)+MOD;
if(ans>=MOD)ans-=MOD;
ans+=calc(l,r-1);
if(ans>=MOD)ans-=MOD;
s.erase(p);//非復活點
}
else
{
int l=*(--it);
it++;
int r=*it;
ans=ans-calc(l,r-1)+MOD;
if(ans>=MOD)ans-=MOD;
ans=ans+calc(l,p-1)+MOD;
if(ans>=MOD)ans-=MOD;
ans+=calc(p,r-1);
if(ans>=MOD)ans-=MOD;//參考公式
s.insert(p);//復活點
}
printf("%lld\n",ans%MOD);
}
return 0;
}