西行妖下
【题目背景】
埋骨于西行妖下,已经多少年了……
幽冥的公主,亡魂的哭泣……
“人间……应该有春吗?”
“反魂蝶,最高八分,最终也无法让西行妖绽放吗……”
【题目描述】
幽幽子站在西行妖下,她需要解封西行妖最后的力量。
西行妖可以当作一个有
n
n
n 个点的树,每个点都有一些符文,初始每个点符文个数为
1
1
1。
幽幽子可以施展魔法,将符文随机移动,来解封封印。
每个点上的符文可以看作是一个
1
m
1~m
1 m 的排列,原本的状态为
1
,
2
,
3
,
4
,
…
…
,
m
1,2,3,4,……,m
1,2,3,4,……,m 按顺序排列
(
m
m
m 为符文的个数)。想要解封一个点上的封印,要求排列中对于任意的
i
i
i,
i
≠
p
i
i\ne p_{i}
i̸=pi 。幽幽子
每次的魔法效果是随机形成一个排列,尝试能否解除封印。
幽幽子每次会走过一条路径,从s 到t,对每个点施展一次魔法,并询问能解封的点的
期望个数。
现在有
Q
Q
Q 次操作:
A
d
d
Add
Add
s
s
s
t
t
t
x
x
x 在
s
s
s 到
t
t
t 的路径上每个点加x 个新的符文。
M
u
l
t
i
Multi
Multi
s
s
s
t
t
t
x
x
x在
s
s
s 到
t
t
t 的路径上,每个点符文个数
×
x
\times x
×x。
Q
u
e
r
y
Query
Query
s
s
s
t
t
t 求从
s
s
s 到
t
t
t 解封点的期望个数是多少。
(注意:每次
Q
u
e
r
y
Query
Query 操作是独立的,即前一次
Q
u
e
r
y
Query
Query 中施展的魔法在Query 结束时被立即撤销,所有走过的路径上点的符文排列变为
p
i
=
i
p_{i}=i
pi=i ,对后续操作不产生影响)
【输入格式】
第一行一个数
n
n
n,表示树的点数。
下面
n
−
1
n-1
n−1 行,每行两个数
u
u
u,
v
v
v,表示有一条边连接
u
u
u 和
v
v
v。
下面一行一个
Q
Q
Q,表示操作次数。
下面
Q
Q
Q 行,每行一个操作:
A
d
d
Add
Add
s
s
s
t
t
t
x
x
x 在
s
s
s 到
t
t
t 的路径上每个点加x 个新的符文。
M
u
l
t
i
Multi
Multi
s
s
s
t
t
t
x
x
x在
s
s
s 到
t
t
t 的路径上,每个点符文个数
×
x
\times x
×x。
Q
u
e
r
y
Query
Query
s
s
s
t
t
t 求从
s
s
s 到
t
t
t 解封点的期望个数是多少。
【输出格式】
对于每次询问
Q
u
e
r
y
Query
Query,输出一行一个实数(为了避免卡精度,所以只需要保留
1
1
1 位小数),
表示解封点的期望个数。
【样例输入】
2
1 2
3
Query 1 2
Add 1 1 1
Query 1 2
【样例输出】
0.0
0.5
【数据范围】
n
≤
80000
,
Q
≤
80000
n\leq 80000,Q\leq 80000
n≤80000,Q≤80000
题解:
首先要知道,设
f
i
f_{i}
fi表示长度为
i
i
i的排列的错排方案,
证明及推导见大神博客
则每个点合法的概率是
f
i
i
!
\frac{f_{i}}{i!}
i!fi
通过严谨的证明(打表 ) 可得,当
i
>
=
20
i>=20
i>=20时,这个值会趋近相等。
于是对于每个点只需要加/乘到大于等于
20
20
20就不用再改了。
然后直接树剖套线段树,由于每个点不会被改超过二十次(乘1特判掉)。
于是区间修改直接暴力化成单点。
#include<bits/stdc++.h>
using namespace std;
#define in inline
#define re register
#define rep(i,a,b) for(re int i=a;i<=b;i++)
#define repd(i,a,b) for(re int i=a;i>=b;i--)
#define For(i,a,b) for(re int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
#define shenben puts("orzlkw");
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
typedef double db;
const db p[30]={
0.00000000000000000000,
0.00000000000000000000,
0.50000000000000000000,
0.33333333333333331000,
0.37500000000000000000,
0.36666666666666664000,
0.36805555555555558000,
0.36785714285714288000,
0.36788194444444444000,
0.36787918871252206000,
0.36787946428571427000,
0.36787943923360589000,
0.36787944132128159000,
0.36787944116069116000,
0.36787944117216193000,
0.36787944117139720000,
0.36787944117144500000,
0.36787944117144217000,
0.36787944117144233000,
0.36787944117144233000,
0.36787944117144233000,
};
const int N=80004;
int n;
struct E{
int to,nxt;
}e[N<<1];
int head[N],tot;
in void ins(int x,int y){
e[++tot]=E{y,head[x]},head[x]=tot;
}
int sz[N],top[N],dep[N],son[N],fa[N];
int pos[N],idx[N],id;
struct T_seg{
struct seg{
int l,r,mn,t1,t2,s;db sum;
}t[N*4];
in void build(int x,int l,int r){
//cout<<x<<" "<<l<<" "<<r<<endl;
t[x].l=l,t[x].r=r;
//cout<<x<<' '<<t[x].l<<" "<<t[x].r<<endl;
t[x].mn=1;
if(l==r){return;}
int mid=l+r>>1;
//cout<<mid<<endl;
build(x<<1,l,mid);build(x<<1|1,mid+1,r);
}
in void upd(int x){
t[x].sum=t[x<<1].sum+t[x<<1|1].sum;
t[x].mn=min(t[x<<1].mn,t[x<<1|1].mn);
}
in void mod(int x,int L,int R,int v,int op){
//pushdown(x);
if(t[x].mn>=20) return;
//cout<<"#"<<t[x].l<<" "<<t[x].mn<<" "<<t[x].r<<endl;
if(t[x].r<L||t[x].l>R) return;
if(t[x].l==t[x].r){
if(op==1){
t[x].sum-=p[t[x].mn],t[x].mn=min(20,t[x].mn+v);
t[x].sum+=p[t[x].mn];
}
else{
t[x].sum-=p[t[x].mn],t[x].mn=min(20,t[x].mn*v);
t[x].sum+=p[t[x].mn];
}
//cout<<t[x].mn<<endl;
//cout<<x<<" "<<t[x].l<<" "<<t[x].r<<" "<<t[x].sum<<endl;
return;
}
int mid=t[x].l+t[x].r>>1;
mod(x<<1,L,R,v,op);
mod(x<<1|1,L,R,v,op);
upd(x);
}
in db Q(int x,int L,int R){
//pushdown(x);
if(t[x].l>=L&&t[x].r<=R) return t[x].sum;
int mid=t[x].l+t[x].r>>1;db res=0;
if(L<=mid) res+=Q(x<<1,L,R);
if(mid<R) res+=Q(x<<1|1,L,R);
return res;
}
}t;
struct T_cut{
in void dfs1(int x,int depth){
dep[x]=depth;sz[x]=1;
for(int i=head[x];i;i=e[i].nxt){
if(e[i].to==fa[x]) continue;
fa[e[i].to]=x;
dfs1(e[i].to,depth+1);
sz[x]+=sz[e[i].to];
if(sz[son[x]]<sz[e[i].to]) son[x]=e[i].to;
}
}
in void dfs2(int x,int chain){
top[x]=chain;pos[x]=++id;idx[id]=x;
if(son[x]) dfs2(son[x],chain);
for(int i=head[x];i;i=e[i].nxt){
if(e[i].to==fa[x]||e[i].to==son[x]) continue;
dfs2(e[i].to,e[i].to);
}
}
in void work(int x,int y,int v,int op){
db ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
if(op==0) ans+=t.Q(1,pos[top[x]],pos[x]);
if(op==1) t.mod(1,pos[top[x]],pos[x],v,1);
if(op==2) t.mod(1,pos[top[x]],pos[x],v,2);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
if(op==0) ans+=t.Q(1,pos[x],pos[y]),printf("%.1lf\n",ans);
if(op==1) t.mod(1,pos[x],pos[y],v,1);
if(op==2) t.mod(1,pos[x],pos[y],v,2);
}
}T;
char s[50];
int main(){
g(n);
For(i,1,n){
int x,y;g(x),g(y);
ins(x,y);ins(y,x);
}
T.dfs1(1,1),T.dfs2(1,1);
t.build(1,1,n);
int QQ;g(QQ);
while(QQ--){
scanf(" %s",s);int x,y;g(x),g(y);
if(s[0]=='Q'){
T.work(x,y,0,0);
}
if(s[0]=='A'){
int z;g(z);
T.work(x,y,z,1);
}
if(s[0]=='M'){
int z;g(z);if(z==1) continue;
T.work(x,y,z,2);
}
}
return 0;
}