comet-2-E(概率DP+基环树+容斥+前缀和)

题目链接

https://www.cometoj.com/contest/37/problem/E

题解

显然是个基环森林,那先考虑树上的情况,是个简单的概率 $DP$ ,当然要做点小容斥

树上缩成点的 $dp$ 值可以作为这个点自然醒来的概率,然后考虑环上的情况。。

考虑环上的任意一点时,该点叫醒谁是不用考虑的,因为自己醒不醒不可能反过来影响自己醒来的概率,这时环已经断成序列。。

所以直接断环复制一份,那么有

这个是个线性递推式,可以写成矩阵乘法的形式,然后用线段树维护乘积或者直接构造逆矩阵维护前缀和什么的。。

不过实在是太不优雅了,然而题解又看不太懂。。

反正要总体考虑写成式子,那么不妨考虑每个点的贡献,有

设后面乘积式的前缀乘积为 $g[i]$ ,有

维护前缀和就可以了。。




代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
*         ┏┓    ┏┓
*         ┏┛┗━━━━━━━┛┗━━━┓
*         ┃       ┃  
*         ┃   ━    ┃
*         ┃ >   < ┃
*         ┃       ┃
*         ┃... ⌒ ...  ┃
*         ┃ ┃
*         ┗━┓ ┏━┛
*          ┃ ┃ Code is far away from bug with the animal protecting          
*          ┃ ┃ 神兽保佑,代码无bug
*          ┃ ┃           
*          ┃ ┃       
*          ┃ ┃
*          ┃ ┃           
*          ┃ ┗━━━┓
*          ┃ ┣┓
*          ┃ ┏┛
*          ┗┓┓┏━━━━━━━━┳┓┏┛
*           ┃┫┫ ┃┫┫
*           ┗┻┛ ┗┻┛
*/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<set>
#include<bitset>
#include<complex>
#include<assert.h>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define NM 200005
#define nm 200005
using namespace std;
const double pi=acos(-1);
const ll inf=998244353;
ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}




struct edge{int t;ll v;edge*next;}e[nm],*h[NM],*o=e;
void add(int x,int y,ll v){o->t=y;o->v=v;o->next=h[x];h[x]=o++;}
int n,c[NM],f[NM],low[NM],tot;
ll a[NM],b[NM],d[NM],_x,_y,ans[NM],cnt;
bool v[NM];

ll qpow(ll x,ll t){return t?qpow(sqr(x)%inf,t>>1)*(t&1?x:1ll)%inf:1ll;}

void tar(int x){
low[x]=_x;
link(x)if(low[j->t]==_x)for(int y=x;y!=f[j->t];y=f[y])c[++tot]=y,v[y]++;
else if(!low[j->t])f[j->t]=x,tar(j->t);
}

void dfs(int x){
d[x]=1;
link(x)if(f[x]!=j->t){
f[j->t]=x;
dfs(j->t);
d[x]=d[x]*(inf+1-j->v*d[j->t]%inf)%inf;
}
ans[x]=d[x]=(a[x]+(inf+1-a[x])*(1-d[x]+inf))%inf;
}


int main(){
n=read();inc(i,1,n)a[i]=read(),a[i]=a[i]*qpow(read(),inf-2)%inf;
inc(i,1,n)f[i]=read();
inc(i,1,n){
ll _t=read();_t=_t*qpow(read(),inf-2)%inf;
add(f[i],i,_t);
}
mem(f);
inc(i,1,n)if(!low[i]){
tot=0;
tar(_x=i);
if(!tot)continue;
inc(i,1,tot){
d[c[i]]=1;
link(c[i])if(!v[j->t]){
f[j->t]=c[i];
dfs(j->t);
d[c[i]]=d[c[i]]*(1-j->v*d[j->t]%inf+inf)%inf;
}
a[c[i]]=(a[c[i]]+(a[c[i]]-1)*(d[c[i]]-1))%inf;
}
c[0]=c[tot];
inc(i,1,tot)link(c[i])if(j->t==c[i-1])b[i]=j->v;
inc(i,tot+1,tot*2)b[i]=b[i-tot],c[i]=c[i-tot];
cnt=0;b[0]=1;
inc(i,1,tot)cnt=cnt*(inf+1-a[c[i]])%inf*b[i]%inf,cnt+=a[c[i]],cnt%=inf,b[i]=b[i-1]*b[i]%inf*(inf+1-a[c[i]])%inf;
inc(i,1,tot){
cnt+=inf-a[c[i]]*b[i+tot-1]%inf*qpow(b[i],inf-2)%inf;cnt%=inf;
cnt=cnt*b[i+tot]%inf*(inf+1-a[c[i]])%inf;cnt+=a[c[i]];cnt%=inf;
ans[c[i]]=cnt;
b[i+tot]=b[i+tot-1]*b[i+tot]%inf*(inf+1-a[c[i]])%inf;
}
}
inc(i,1,n)printf("%lld ",ans[i]);putchar('\n');
return 0;
}