[JZOJ5987] 仙人掌毒题

Description

Solution

套路题...

全他娘的是套路...

首先如何处理仙人掌,可以在线拿 \(lct\) 维护,或者离线之后树剖。(\(lct\) 维护太毒了写不来,就离线树剖了又好写又不用调

离线树剖的意思就是按加边时间为权值建出一棵最小生成树(或者生成森林),这就是最终的仙人掌的一棵生成树了。

加边的时候如果这是条树边那就直接加上,如果不是树边,那就在线段树上查一下这个环是否有边被覆盖,只要有一条边被覆盖那就不满足仙人掌性质,那就不能加这条边进去,否则加进去然后线段树区间赋值就好了。

然后怎么求期望联通块数?

如果这是个森林的话,期望联通块数=期望点数-期望边数

那要是个沙漠呢?

考虑一下森林的这个式子是怎么来的,就是最开始有点数个联通块,然后每加一条边就合并两个联通块,所以是点数-边数。

那放到仙人掌上,发现每条形成环的非树边都被多减了一次,那再加回来就好了。而成环的非树边就恰好等于仙人掌的环数,因为一条边最多只会出现在一个环里。所以沙漠的期望联通块数=期望点数-期望边数+期望环数。

然后分开考虑就好了,因为点还有颜色,那我们规定一条边是白的当且仅当两端点都是白色,一个环是白的当且仅当环上的所有点都是白色。黑色同理。

先算点数。根据期望的线性性,一个点是白色点的概率就是 \((\frac{n-1}n)^t\),那对期望的贡献就是 \(1\cdot (\frac{n-1}n)^t\) ,黑色点拿 \(1\) 减一下就行了。

再算边数。一条边是白色边的概率是 \((\frac{n-2}n)^t\) ,是黑色边的概率容斥一下,就是 \(2\cdot(1-(\frac{n-1}n)^t)-(1-(\frac{n-2}n)^t)=1+(\frac{n-2}n)^t-2\cdot (\frac{n-1}n)^t\) ,含义画一个Venn图就很清楚了。

然后是环数,白色环的概率很好算,就是 \((\frac{n-len}n)^t\),其中 \(len\) 是环长。那黑色环呢,也是容斥一下,枚举至少有 \(j\) 个白点,所以 \(f(len)=\sum\limits_{j=0}^{len} (-1)^j \cdot C_{len}^j\cdot (\frac{n-j}n)^t\)

嗯然后就做完了。

Code

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using std::swap;
typedef long long ll;
#define int long long
const int N=3e5+5;
const int mod=998244353;

int n,m,t,w;
int fac[N],ifac[N];
int cnt,head[N],is[N];
int father[N],ques[N][2];

struct Edge{
    int to,nxt;
}edge[N<<1];

void add(int x,int y){
    edge[++cnt].to=y;
    edge[cnt].nxt=head[x];
    head[x]=cnt;
}

namespace slpf{
    #define ls x<<1
    #define rs x<<1|1
    #define lss ls,l,mid,ql,qr
    #define rss rs,mid+1,r,ql,qr
    int sze[N],son[N],fa[N];    
    int flag[N<<2],sum[N<<2];
    int tot,dfn[N],top[N],d[N];

    void dfs(int now){
        sze[now]=1;
        for(int i=head[now];i;i=edge[i].nxt){
            int to=edge[i].to;
            if(sze[to]) continue;
            fa[to]=now;d[to]=d[now]+1;
            dfs(to);sze[now]+=sze[to];
            son[now]=sze[to]>sze[son[now]]?to:son[now];
        }
    }

    void dfs2(int now,int low){
        dfn[now]=++tot;top[now]=low;
        if(son[now]) dfs2(son[now],low);
        for(int i=head[now];i;i=edge[i].nxt){
            int to=edge[i].to;
            if(dfn[to]) continue;
            dfs2(to,to);
        }
    }

    void pushup(int x){
        sum[x]=sum[ls]|sum[rs];
    }

    void pushdown(int x){
        if(!flag[x]) return;
        flag[ls]=flag[rs]=sum[ls]=sum[rs]=1;
        flag[x]=0;
    }

    void modify(int x,int l,int r,int ql,int qr){
        if(ql<=l and r<=qr) return flag[x]=sum[x]=1,void();
        pushdown(x);int mid=l+r>>1;
        if(ql<=mid) modify(lss);
        if(mid<qr) modify(rss);
        pushup(x);
    }

    int query(int x,int l,int r,int ql,int qr){
        if(ql<=l and r<=qr) return sum[x];
        pushdown(x);int mid=l+r>>1,ans=0;
        if(ql<=mid) ans|=query(lss);
        if(mid<qr) ans|=query(rss);
        return ans;
    }

    int ask(int x,int y){
        int ans=0;
        while(top[x]!=top[y]){
            if(d[top[x]]<d[top[y]]) swap(x,y);
            ans|=query(1,1,n,dfn[top[x]],dfn[x]);
            x=fa[top[x]];
        } if(d[x]<d[y]) swap(x,y);
        if(x!=y) ans|=query(1,1,n,dfn[y]+1,dfn[x]);
        return ans;
    }

    int change(int x,int y){
        while(top[x]!=top[y]){
            if(d[top[x]]<d[top[y]]) swap(x,y);
            modify(1,1,n,dfn[top[x]],dfn[x]);
            x=fa[top[x]];
        } if(d[x]<d[y]) swap(x,y);
        if(x!=y) modify(1,1,n,dfn[y]+1,dfn[x]);
        return y;
    }
}

int ksm(int a,int b=mod-2,int ans=1){
    while(b){
        if(b&1) ans=ans*a%mod;
        a=a*a%mod; b>>=1;
    } return ans;
}

int getint(){
    int X=0,w=0;char ch=getchar();
    while(!isdigit(ch))w|=ch=='-',ch=getchar();
    while( isdigit(ch))X=X*10+ch-48,ch=getchar();
    if(w) return -X;return X;
}

int find(int x){
    return father[x]==x?x:father[x]=find(father[x]);
}

int dec(int x,const int &y){
    return x-y<0?x-y+mod:x-y;
}

int C(int n,int m){
    return fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}

signed main(){
    freopen("cactus.in","r",stdin);freopen("cactus.out","w",stdout);
    gi(n),gi(m),gi(t),gi(w);
    fac[0]=ifac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
    ifac[n]=ksm(fac[n]);
    for(int i=n-1;i;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
    for(int i=1;i<=n;i++) father[i]=i;
    int invn=ksm(ksm(n),t),baid=ksm(n-1,t)*invn%mod,baib=ksm(n-2,t)*invn%mod,heib=(baib-baid*2%mod+1+mod)%mod;
    for(int i=1;i<=m;i++){
        gi(ques[i][0]),gi(ques[i][1]);
        int r1=find(ques[i][0]),r2=find(ques[i][1]);
        if(r1!=r2) father[r1]=r2,is[i]=1,add(ques[i][0],ques[i][1]),add(ques[i][1],ques[i][0]);
    } for(int i=1;i<=n;i++) if(!slpf::dfn[i]) slpf::d[i]=1,slpf::dfs(i),slpf::dfs2(i,i);
    int ans=(w==1?n:baid*n%mod);
    for(int i=1;i<=m;i++){
        if(is[i]){
            ans=dec(ans,baib);
            if(w==1) ans=dec(ans,heib);
        } else{
            if(!slpf::ask(ques[i][0],ques[i][1])){
                int z=slpf::change(ques[i][0],ques[i][1]);
                ans=dec(ans,baib);
                if(w==1) ans=dec(ans,heib);
                int len=slpf::d[ques[i][0]]+slpf::d[ques[i][1]]-slpf::d[z]*2+1;
                (ans+=ksm(n-len,t)*invn%mod)%=mod;
                if(w==1){
                    int res=0;
                    for(int j=0;j<=len;j++)
                        (res+=(j&1?mod-C(len,j):C(len,j))*ksm(n-j,t)%mod*invn%mod)%=mod;
                    (ans+=res)%=mod;
                }
            }
        } print(ans),putc('\n');
    } return io::flush(),0;
}
posted @ 2019-01-19 16:02  YoungNeal  阅读(305)  评论(0编辑  收藏  举报