代码描述
幽幽子站在西行妖下,她需要解封西行妖最后的力量。
西行妖可以当作一个有 n 个点的树,每个点都有一些符文,初始每个点符文个数为 1。幽幽子可以施展魔法,将符文随机移动,来解封封印。
每个点上的符文可以看作是一个 1~m 的排列,原本的状态为 1,2,3,4,……,m 按顺序排列(m 为符文的个数)。想要解封一个点上的封印,要求排列中对于任意的i , p i 。幽幽子每次的魔法效果是随机形成一个排列,尝试能否解除封印。幽幽子每次会走过一条路径,从 s 到 t,对每个点施展一次魔法,并询能解封的点的期望个数。
现在有 Q 次操作:
Add s t x 在 s 到 t 的路径上每个点加 x 个新的符文。
Multi s t x 在 s 到 t 的路径上,每个点符文个数*x。
Query s t 求从 s 到 t 解封点的期望个数是多少。
(注意:每次 Query 操作是独立的,即前一次 Query 中施展的魔法在 Query 结束时被立即撤销,所有走过的路径上点的符文排列变为 p i,对后续操作不产生影响)
题解
错排容斥式子:n!(1−1/1!+1/2!−1/3!+...)n!(1-1/1!+1/2!-1/3!+...)n!(1−1/1!+1/2!−1/3!+...)
所以期望是1−1/1!+1/2!−1/3!+...1-1/1!+1/2!-1/3!+...1−1/1!+1/2!−1/3!+...
显然n特别大的时候这个式子的值就趋于定值了,结合题目的精度要求开到20就可以了,代码实现上因为一个数最多被操作十几次所以直接暴力单点修改比较好写
代码
#include <bits/stdc++.h>
#define maxn 1000005
#define lo o<<1
#define ro o<<1|1
#define mid (l+r>>1)
#define INF 0x3f3f3f3f
using namespace std;
int read(){
int res,f=1; char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1; res=(c^48);
while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
return res*f;
}
struct EDGE{
int u,v,nxt;
}e[maxn<<1];
struct TR{
int w; double v;
bool tag;
TR(){tag=w=v=0;}
}tr[maxn<<2];
int n,m,q,cnt,head[maxn];
void add(int u,int v){
e[++cnt]=(EDGE){u,v,head[u]};
head[u]=cnt;
}
int tim,size[maxn],son[maxn],top[maxn],dep[maxn],dfn[maxn],fa[maxn];
double jc=1,ans[maxn];
void DFS1(int u){
size[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v; if(size[v]) continue;
fa[v]=u; dep[v]=dep[u]+1; DFS1(v); size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void DFS2(int u,int tp){
dfn[u]=++tim; top[u]=tp;
if(son[u]) DFS2(son[u],tp);
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v; if(dfn[v]) continue;
DFS2(v,v);
}
}
void pushup(int o){
tr[o].v=tr[lo].v+tr[ro].v;
tr[o].tag=tr[lo].tag&tr[ro].tag;
}
void build(int o,int l,int r){
if(l==r){
tr[o].w=1;
return ;
}
build(lo,l,mid); build(ro,mid+1,r);
pushup(o);
}
void add(int o,int l,int r,int ll,int rr,int w){
if(l>rr || ll>r || tr[o].tag) return ;
if(l==r){
tr[o].w+=w;
if(tr[o].w>=20) tr[o].w=20,tr[o].tag=1;
tr[o].v=ans[tr[o].w];
return ;
}
add(lo,l,mid,ll,rr,w); add(ro,mid+1,r,ll,rr,w);
pushup(o);
}
void mu(int o,int l,int r,int ll,int rr,int w){
if(l>rr || ll>r || tr[o].tag) return ;
if(l==r){
tr[o].w*=w;
if(tr[o].w>=20) tr[o].w=20,tr[o].tag=1;
tr[o].v=ans[tr[o].w];
return ;
}
mu(lo,l,mid,ll,rr,w); mu(ro,mid+1,r,ll,rr,w);
pushup(o);
}
double query(int o,int l,int r,int ll,int rr){
if(l>rr || ll>r) return 0;
if(ll<=l && r<=rr) return tr[o].v;
return query(lo,l,mid,ll,rr)+query(ro,mid+1,r,ll,rr);
}
void update_path(int type,int x,int y,int w){
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]) swap(x,y);
if(type) add(1,1,n,dfn[top[y]],dfn[y],w);
else mu(1,1,n,dfn[top[y]],dfn[y],w);
y=fa[top[y]];
}
if(dep[x]>dep[y]) swap(x,y);
if(type) add(1,1,n,dfn[x],dfn[y],w);
else mu(1,1,n,dfn[x],dfn[y],w);
}
double query_path(int x,int y){
double res=0;
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]) swap(x,y);
res+=query(1,1,n,dfn[top[y]],dfn[y]);
y=fa[top[y]];
}
if(dep[x]>dep[y]) swap(x,y);
res+=query(1,1,n,dfn[x],dfn[y]);
return res;
}
int main(){
ans[0]=1; for(int i=1;i<=20;i++) jc/=i,ans[i]=ans[i-1]+(i&1?-jc:jc);
n=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y); add(y,x);
}
q=read();
DFS1(1);
DFS2(1,1);
build(1,1,n);
while(q--){
char s[100]; scanf("%s",s+1);
if(s[1]=='A'){
int x=read(),y=read(),w=read(); if(w==0) continue;
update_path(1,x,y,w);
}
else if(s[1]=='M'){
int x=read(),y=read(),w=read(); if(w==1) continue;
update_path(0,x,y,w);
}
else{
int x=read(),y=read();
printf("%.1lf\n",query_path(x,y));
}
}
}