西行妖下

代码描述

幽幽子站在西行妖下,她需要解封西行妖最后的力量。
西行妖可以当作一个有 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!(11/1!+1/2!1/3!+...)
所以期望是1−1/1!+1/2!−1/3!+...1-1/1!+1/2!-1/3!+...11/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));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarden_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值