20181101

T1 4019: 白玉楼前(youmu)

题目描述

妖梦现在要玩幽幽子的游戏,她才能拿回自己的半灵。
游戏规则是这样的:
幽幽子有 n 个点,现在她让妖梦对每个点随机一条出边(随机到每个点的概率都相等),然后得到一张图。(注意:可以自环)
如果这张图任意一个点沿着边走两步(显然这样的走法唯一)都能到达自身,则幽幽子可以通关。
现在幽幽子想问妖梦,她通关的概率是多少?
两个图不同,当且仅当存在一条边出现在图 A 中且不出现在图 B 中。图中的点有编号,边无编号。答案 mod 998244353。

题解

神犇都想到题解了(所以我没想到)
考虑dp,设fif_i表示前i个点已经匹配完的方案数
fi=fi1+(i1)fi2f_i=f_{i-1}+(i-1)f_{i-2}
预处理f数组即可,最后乘(1/n)n(1/n)^n
具体见代码

#include <bits/stdc++.h>
#define E register
using namespace std;
const int P=998244353,N=5e5+5;
int T,n,jc[N],ny[N],f[N],ans[N];
inline int K(E int x,E int y){
    int A=1;while(y){
        if (y&1) A=1ll*A*x%P;
        x=1ll*x*x%P;y>>=1;
    }
    return A;
}
int main(){
    f[0]=f[1]=1;
    for (int i=2;i<N;i++)
        f[i]=(f[i-1]+1ll*(i-1)*f[i-2]%P)%P;
    for (scanf("%d",&T);T--;)
        scanf("%d",&n),
        printf("%d\n",1ll*K(K(n,P-2),n)*f[n]%P);
    return 0;
}

T2 4020: 式神守护(yukari)

题目描述

紫妈有n 个隙间排成一列,每个隙间都有一个权值val 。
她可以选出某些隙间来召唤式神:一组隙间能成功召唤式神当且仅当他们的权值和为m的倍数。(可以是0 倍)
现在紫妈试图召唤 Q次式神,每次给出一个l和r ,她试图在第l到r个隙间中召唤式神,她会选择其中一些隙间(不一定需要连续的一些)召唤式神。她想知道,有多少种方案可以成功召唤式神。

题解

考场只想到了线段树,然鹅效率不优秀(只有O(nlognm2)O(nlogn·m^2))
考虑分治
设要处理[l,r][l,r]这一段区间内的答案
我们可以把询问没有跨过mid的递归下去求解,则留在这段区间的询问,左端点在[l,mid][l,mid]内,右端点在[mid+1,r][mid+1,r]
考虑dpdp
f[i][j]f[i][j]表示从midmid向左到ii,余数为jj的方案数
g[i][j]g[i][j]表示从mid+1mid+1向右到ii,余数为jj的方案数
可以很快得到dp式子:
f[i][j]=f[i+1][j]+f[i+1][(ja[i]+m)f[i][j]=f[i+1][j]+f[i+1][(j-a[i]+m)%m]]
g[i][j]=g[i1][j]+g[i1][(ja[i]+m)g[i][j]=g[i-1][j]+g[i-1][(j-a[i]+m)%m]]
所以对于一个跨过midmid的询问[L,R][L,R],我们可以得到他的答案
ans=j=0m1f[L][j]g[R][(mj)ans=\sum_{j=0}^{m-1}f[L][j]*g[R][(m-j)%m]]
效率O(nlognm)O(nlogn·m)
具体见代码

#include <bits/stdc++.h>
#define I inline
#define _(d) while(d(isdigit(c=getchar())))
using namespace std;
I int R(){
    int x;char c;_(!);x=(c^48);
    _()x=(x<<3)+(x<<1)+(c^48);return x;
}
const int N=2e5+5,P=1e9+7;
int n,m,a[N],q,s[N],f[N][20],g[N][20];
struct O{int l,r,d;}p[N],h[N];
void solve(int l,int r,int ql,int qr){
    if (l==r){
        for (int i=ql;i<=qr;i++)
            s[p[i].d]=(a[l]<1)+1;
        return;
    }
    int mid=l+r>>1,cl=ql,cr=qr,kl;
    for (int i=ql;i<=qr;i++){
        if (p[i].r<=mid) h[cl++]=p[i];
        if (p[i].l>mid) h[cr--]=p[i];
    }
    kl=cl;
    for (int i=ql;i<=qr;i++)
        if (p[i].l<=mid && p[i].r>mid) h[kl++]=p[i];
    for (int i=ql;i<=qr;i++) p[i]=h[i];
    if (cl>ql) solve(l,mid,ql,cl-1);
    if (cr<qr) solve(mid+1,r,cr+1,qr);
    f[mid][0]=1;for (int j=1;j<m;j++) f[mid][j]=0;
    g[mid+1][0]=1;for (int j=1;j<m;j++) g[mid+1][j]=0;
    f[mid][a[mid]]++;g[mid+1][a[mid+1]]++;
    for (int i=mid-1;i>=l;i--) for (int j=0;j<m;j++)
        f[i][j]=(f[i+1][j]+f[i+1][(j-a[i]+m)%m])%P;
    for (int i=mid+2;i<=r;i++) for (int j=0;j<m;j++)
        g[i][j]=(g[i-1][j]+g[i-1][(j-a[i]+m)%m])%P;
    for (int i=cl;i<=cr;i++) for (int j=0;j<m;j++)
        s[p[i].d]=(s[p[i].d]+1ll*f[p[i].l][j]*g[p[i].r][(m-j)%m]%P)%P;
}
int main(){
    n=R();m=R();for (int i=1;i<=n;i++) a[i]=R()%m;
    q=R();for (int i=1;i<=q;i++) p[i].l=R(),p[i].r=R(),p[i].d=i;
    solve(1,n,1,q);
    for (int i=1;i<=q;i++) printf("%d\n",s[i]);
    return 0;
}

T3 4021: 西行妖下(yuyuko)

题目描述

幽幽子站在西行妖下,她需要解封西行妖最后的力量。
西行妖可以当作一个有n 个点的树,每个点都有一些符文,初始每个点符文个数为1。
幽幽子可以施展魔法,将符文随机移动,来解封封印。每个点上的符文可以看作是一个1~m 的排列,原本的状态为1,2,3,4,……,m 按顺序排列(m 为符文的个数)。想要解封一个点上的封印,要求排列中对于任意的i,pi !=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=i,对后续操作不产生影响)

题解

来自百度百科的错排详情
可以把期望值看成错排方案数/排列数
因为错排方案数为n![1/0!1/1!+1/2!1/3!+1/4!+...+(1)n/n!]n! ·[1/0!-1/1!+1/2!-1/3!+1/4!+...+(-1)^n/n!]
排列数为n!n!,所以期望值为1/0!1/1!+1/2!1/3!+1/4!+...+(1)n/n!1/0!-1/1!+1/2!-1/3!+1/4!+...+(-1)^n/n!
当n很大的时候约等于1/e,所以我们只需要求出当n不超过20时的期望值,超过20的当做20来做,然后可以区间修改啦
其实因为每个数修改得不会超过20,所以我们可以直接单点修改就好了
具体见代码

#include <bits/stdc++.h>
#define Ls k<<1
#define Rs Ls|1
#define db double
#define I inline
#define E register
using namespace std;
const int N=80005;
int tt,t,n,q,id[N],head[N],V[N*2],nex[N*2],fa[N],son[N],sz[N],top[N],dep[N];
struct T{db p;int w;}a[N*4];char str[7];db F[21],f[21],jc[21];
I void add(E int u,E int v){V[++t]=v;nex[t]=head[u];head[u]=t;}
I void dfs1(E int x,E int fat,E int deep){
    fa[x]=fat;dep[x]=deep;sz[x]=1;
    for (int i=head[x];i;i=nex[i]){
        if (V[i]==fat) continue;
        dfs1(V[i],x,deep+1);sz[x]+=sz[V[i]];
        if (sz[V[i]]>sz[son[x]]) son[x]=V[i];
    }
}
I void dfs2(E int x,E int tp){
    top[x]=tp;id[x]=++tt;
    if (son[x]) dfs2(son[x],tp);
    for (E int i=head[x];i;i=nex[i])
        if (V[i]^fa[x] && V[i]^son[x])
            dfs2(V[i],V[i]);
}
I void build(E int k,E int l,E int r){
    a[k].w=1;a[k].p=0.0;if (l==r) return;
    int mid=l+r>>1;build(Ls,l,mid);
    build(Rs,mid+1,r);
}
I void update(E int k,E int l,E int r,E int L,E int R,E int v,E bool ty){
    if (l==r){
        if (ty) a[k].w=min(20,a[k].w+v);
        else a[k].w=min(20,a[k].w*v);
        a[k].p=F[a[k].w];return;
    }
    int mid=l+r>>1;
    if (mid>=L && a[Ls].w<20) update(Ls,l,mid,L,R,v,ty);
    if (mid<R && a[Rs].w<20) update(Rs,mid+1,r,L,R,v,ty);
    a[k].w=min(a[Ls].w,a[Rs].w);a[k].p=a[Ls].p+a[Rs].p;
}
I void update_chain(E int x,E int y,E bool ty,E int v){
    while(top[x]!=top[y]){
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        update(1,1,n,id[top[x]],id[x],v,ty);x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    update(1,1,n,id[x],id[y],v,ty);
}
I db query(E int k,E int l,E int r,E int L,E int R){
    if (L<=l && r<=R) return a[k].p;
    int mid=l+r>>1;
    if (mid<L) return query(Rs,mid+1,r,L,R);
    if (mid>=R) return query(Ls,l,mid,L,R);
    return query(Ls,l,mid,L,R)+query(Rs,mid+1,r,L,R);
}
I void query_chain(E int x,E int y){
    db j=0.0;
    while(top[x]!=top[y]){
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        j=j+query(1,1,n,id[top[x]],id[x]);x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    printf("%.1lf\n",j+query(1,1,n,id[x],id[y]));
}
int main(){
    f[0]=jc[0]=jc[1]=1.0;f[1]=0.0;
    for (int i=2;i<21;i++)
        f[i]=1.0*(i-1)*(f[i-1]+f[i-2]),
        jc[i]=1.0*i*jc[i-1],F[i]=f[i]/jc[i];
    scanf("%d",&n);
    for (E int u,v,i=1;i<n;i++)
        scanf("%d%d",&u,&v),add(u,v),add(v,u);
    dfs1(1,0,1);dfs2(1,1);build(1,1,n);
    for (scanf("%d",&q);q--;){
        scanf("%s",str);int x,y,z;
        if (str[0]=='A'){
            scanf("%d%d%d",&x,&y,&z);
            if (z) update_chain(x,y,1,z);
        }
        else if (str[0]=='M'){
            scanf("%d%d%d",&x,&y,&z);
            if (z>1) update_chain(x,y,0,z);
        }
        else scanf("%d%d",&x,&y),query_chain(x,y);
    }
    return 0;
}
posted @ 2018-11-07 20:01  xjqxjq  阅读(148)  评论(0编辑  收藏  举报