jzoj5987. 【WC2019模拟2019.1.4】仙人掌毒题 (树链剖分+概率期望+容斥)

题面

题解

又一道全场切的题目我连题目都没看懂……细节真多……

先考虑怎么维护仙人掌。在线可以用LCT,或者像我代码里先离线,并按时间求出一棵最小生成树(或者一个森林),然后树链剖分。如果一条边不是生成树上的边,它肯定会和树上\(u,v\)这条路径构成一个环,然后对于每条树边记录一下这条树边被覆盖过没有。如果\(u,v\)路径上有任何一条树边被覆盖过,那么就说明路径上有一条边已经在一个简单环中,这条非树边就不能加。否则就加上这条边并让这条路径上所有树边的覆盖次数加一

然后考虑期望连通块个数。首先根据我也不知道是什么的期望的线性,可以分开考虑。比方说一个森林,连通块个数等于点数\(-\)边数,那么期望连通块个数就是期望点数\(-\)期望边数。

然后考虑沙漠,沙漠的连通块个数为点数\(-\)边数\(+\)环数。可以这么考虑,如果没有环,那么就是点数\(-\)边数,而对于一条加进去会成环的边,它被多减了一次,那么就要加上去,而多减的边数就是环数

于是只要分别考虑期望点数,期望边数,期望环数即可。顺便黑白也可以分开考虑,即如果\(w=0\)的话只需要考虑白点就可以了

设\(0\)点为白点,\(1\)点为黑点,那么对于一个点,它在染完色之后仍为白色的概率为\((\frac{n-1}{n})^t\),那么白点的期望个数就是\(n\times (\frac{n-1}{n})^t\),为黑点的概率就是\(1\)减去为白点的概率,黑点期望个数同理

然后考虑边为白色的概率,就是\((\frac{n-2}{n})^t\),边为黑色的概率的话,根据容斥原理,为\(1-2(\frac{n-1}{n})^t+(\frac{n-2}{n})^t\)

然后是环,设环上点数为\(p\),那么环为白色的概率为\((\frac{n-p}{n})^t\)。然而环为黑色的概率怎么算?记\(f_i\)为\(i\)个点全黑的概率,\(g_i\)为\(i\)个点全白的概率,那么有转移\(f_i=1-\sum_{j=0}^{i-1}f_jg_{i-j}{i\choose j}\),就是枚举有多少个黑点并容斥,可以用多项式求逆解决

然而多项式求逆太烦了……我们可以用容斥原理计算\(f_p\),有\[f_p=\sum_{j=0}^p(-1)^j{p\choose j}(\frac{n-j}{n})^t\]
因为\(\sum p=O(n)\),所以这里的复杂度是没有问题的(\(\sum p=O(n)\)的话,是因为生成树上每条边最多在一个环中,每个环的点数等于边数,所以环上总点数\(=\)生成树上边数\(+\)成环的边数,成环边数不会大于\(O(n)\),所以有\(\sum p=O(n)\))

总的时间复杂度为\(O(n\log t+m\log^2n)\)

因为码量有点大,所以代码分块了……这是我这几天来唯一没有压行的代码了……

//minamoto
#include<bits/stdc++.h>
#define R register
#define ls (p<<1)
#define rs (p<<1|1)
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=1e5+5,M=2.5e5+5,P=998244353;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
    return res;
}
int n,m,t,w;
struct seg{
    struct eg{int v,nx;}e[N<<1];int head[N],tot;
    inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
    int dfn[N],top[N],sz[N],fa[N],dep[N],son[N],cnt;
    void dfs1(int u){
        sz[u]=1,dep[u]=dep[fa[u]]+1;
        go(u)if(v!=fa[u]){
            fa[v]=u,dfs1(v),sz[u]+=sz[v];
            if(sz[v]>sz[son[u]])son[u]=v;
        }
    }
    void dfs2(int u,int t){
        top[u]=t,dfn[u]=++cnt;if(!son[u])return;
        dfs2(son[u],t);
        go(u)if(v!=fa[u]&&v!=son[u])dfs2(v,v);
    }
    bool tag[N<<2],vis[N<<2];
    void pd(int p){
        if(tag[p]){
            tag[ls]=tag[rs]=1;
            vis[ls]=vis[rs]=1;
            tag[p]=0;
        }
    }
    void update(int p,int l,int r,int ql,int qr){
        if(ql<=l&&qr>=r)return (void)(tag[p]=vis[p]=1);
        int mid=(l+r)>>1;pd(p);
        if(ql<=mid)update(ls,l,mid,ql,qr);
        if(qr>mid)update(rs,mid+1,r,ql,qr);
        vis[p]=vis[ls]|vis[rs];
    }
    bool query(int p,int l,int r,int ql,int qr){
        if(ql<=l&&qr>=r)return vis[p];
        int mid=(l+r)>>1;pd(p);
        if(ql<=mid&&query(ls,l,mid,ql,qr))return true;
        if(qr>mid&&query(rs,mid+1,r,ql,qr))return true;
        return false;
    }
    void change_path(int u,int v){
        while(top[u]!=top[v]){
            if(dep[top[u]]<dep[top[v]])swap(u,v);
            update(1,1,n,dfn[top[u]],dfn[u]),u=fa[top[u]];
        }if(dep[u]<dep[v])swap(u,v);
        if(u!=v)update(1,1,n,dfn[son[v]],dfn[u]);
    }
    bool query_path(int u,int v){
        while(top[u]!=top[v]){
            if(dep[top[u]]<dep[top[v]])swap(u,v);
            if(query(1,1,n,dfn[top[u]],dfn[u]))return false;
            u=fa[top[u]];
        }if(dep[u]<dep[v])swap(u,v);
        if(u!=v&&query(1,1,n,dfn[son[v]],dfn[u]))return false;
        return true;
    }
    int LCA(int u,int v){
        while(top[u]!=top[v]){
            if(dep[top[u]]<dep[top[v]])swap(u,v);
            u=fa[top[u]];
        }return dep[u]<dep[v]?u:v;
    }
    int dis(int u,int v){return dep[u]+dep[v]-(dep[LCA(u,v)]<<1)+1;}
    void init(){
        fp(i,1,n)if(!dfn[i])dfs1(i),dfs2(i,i);
    }
}T;
struct QAQ{
    struct eg{int u,v,is;}e[M];
    int fa[N],inv[N],fac[N];
    int ndwh,ndbl,edwh,edbl,ciwh,cibl,invn,res,u,v,p,gg,invp;
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    inline int C(R int n,R int m){return m>n?0:1ll*fac[n]*inv[m]%P*inv[n-m]%P;}
    int merge(int u,int v){
        u=find(u),v=find(v);
        if(u==v)return false;
        return fa[u]=v,true;
    }
    void init(){
        invn=ksm(n,P-2);
        ndwh=ksm(mul(n-1,invn),t);
        ndbl=dec(1,ndwh);
        edwh=ksm(mul(n-2,invn),t);
        edbl=add(1,dec(edwh,mul(2,ndwh)));
        res=(w==1)?n:mul(ndwh,n);
        fac[0]=inv[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
        inv[n]=ksm(fac[n],P-2);fd(i,n-1,1)inv[i]=mul(inv[i+1],i+1);
    }
    void MAIN(){
        n=read(),m=read(),t=read(),w=read();
        init();
        fp(i,1,n)fa[i]=i;
        fp(i,1,m){
            e[i].u=read(),e[i].v=read();
            if(merge(e[i].u,e[i].v)){
                e[i].is=1,T.add(e[i].u,e[i].v),T.add(e[i].v,e[i].u);
            }
        }
        T.init();
        fp(i,1,m){
            if(e[i].u!=e[i].v){
                u=e[i].u,v=e[i].v;
                if(!e[i].is){
                    if(T.query_path(u,v)){
                        T.change_path(u,v);
                        res=dec(res,edwh);
                        if(w==1)res=dec(res,edbl);
                        p=T.dis(u,v),invp=ksm(p,P-2);
                        res=add(res,ksm(mul(n-p,invn),t));
                        if(w==1){
                            gg=0;
                            for(R int j=0,ty=1;j<=p;++j,ty=P-ty)
                                gg=add(gg,1ll*ty*C(p,j)%P*ksm(mul(n-j,invn),t)%P);
                            res=add(res,gg);
                        }
                    }
                }else{
                    res=dec(res,edwh);
                    if(w==1)res=dec(res,edbl);
                }
            }print(res);
        }
    }
}loli;
int main(){
//  freopen("testdata.in","r",stdin);
//  freopen("testdata.out","w",stdout);
    freopen("cactus.in","r",stdin);
    freopen("cactus.out","w",stdout);
    loli.MAIN();
    return Ot(),0;
}

原文地址:https://www.cnblogs.com/bztMinamoto/p/10223695.html

时间: 2024-10-14 22:52:02

jzoj5987. 【WC2019模拟2019.1.4】仙人掌毒题 (树链剖分+概率期望+容斥)的相关文章

模拟赛T2 仙人掌毒题

分析: 我们首先知道这样一个公式: 对于树:连通块=点-边 对于仙人掌:连通块=点-边+环 考虑期望的线性性: 连通块期望=点期望-边期望+环期望 对于点: 一个点T次标记后还为0的概率为\((\frac{n-1}{n})^T\),为1就是\(1-(\frac{n-1}{n})^T\) 乘上系数n就是点期望 对于边: 一条边两边点都是0的概率为\((\frac{n-2}{n})^T\) 为1要容斥一下,概率为\(1-2(\frac{n-1}{n})^T+(\frac{n-2}{n})^T\) 然

2019年ICPC南昌网络赛 J. Distance on the tree 树链剖分+主席树

边权转点权,每次遍历到下一个点,把走个这条边的权值加入主席树中即可. #include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> using namespace std; const int maxx = 2e5+10; struct node{ int l,r,cnt; }tree[maxx*40]; int head[maxx],rk[maxx],siz[maxx

csp-s模拟测试59(10.4)「Reverse」(set)&#183;「Silhouette」(容斥)

A. Reverse 菜鸡wwb又不会了..... 可以线段树优化建边,然而不会所以只能set水了 发现对于k和当前反转点固定的节点x确定奇偶性所到达的节点奇偶性是一定的 那么set维护奇偶点,然后每次set找点删点注意边界 set在删点后原来的迭代器会玄学出错,xuefeng好像被坑了,所以lowerbound一下就不用++了 B. Silhouette 很玄学的容斥 考场多QJ了18分,因为如果1-n是个序列,好像就是一个简单的容斥..... 然后用能发现是以“L”形的形状向右推的,随便乘一

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval 题目大意:给一个长度为n,值域为[1, n]的序列{a},要求支持m次操作: 单点修改 1 pos val 询问子区间中某个值域的数的个数,连续的相同数字只记为一个.(即统计数字段的个数) 2 L R x y 数据范围: 1 ≤ n,m ≤ 2×10^5 1 ≤ a[i] ≤ n 解题思路: 连续重复的数字只记一次.所以考虑将每个数字段除第一个出现外的数字都删去(记为0).在读入操作的时候暴力模拟,同时维护

jzoj5991. 【北大2019冬令营模拟2019.1.6】Juice

题面 题解 好迷-- //minamoto #include<bits/stdc++.h> #define R register #define ll long long #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i) #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i) #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)

jzoj5990. 【北大2019冬令营模拟2019.1.6】Bear (状压dp)

题面 题解 我永远讨厌dp.jpg 搞了一个下午优化复杂度最后发现只要有一个小trick就可以A了→_→.全场都插头dp就我一个状压跑得贼慢-- 不难发现我们可以状压,对于每一行,用状态\(S\)表示有哪些格子是已经被上一行推倒了的,那么我们可以枚举本行所有格子的字母情况,然后计算一下这个时候下一行格子被推倒的情况,把这一行的贡献加到下一行就行了. 简单来说就是记一个\(f[pos][S]\)表示第\(pos\)行,格子被推倒的情况为\(S\)时的方案数,\(dp[pos][S]\)为所有方案中

jzoj6002. 【PKUWC2019模拟2019.1.15】Permutation (组合数)

题面 题解 设\(lim=(n-1)/2\)(这里是下取整),那么\(x\)位置的值最大不能超过\(lim\),而\(y\)处的值不能小于\(y\),于是有\[Ans=\sum_{i=1}^{lim}\sum_{j=2 i+1}^n(y-2)!{j-2\choose y-2}(n-y)!\] 上式的意思是,枚举\(x\)处的值\(i\)和\(y\)处的值\(j\),那么放在\(y\)前面的数都不能大于\(j\),要从除了\(i,j\)之外的剩下\(j-2\)个数中选出\(y-2\)个,因为顺序无

jzoj6009. 【THUWC2019模拟2019.1.18】Counting (dp)

Description 羽月最近发现,她发动能力的过程是这样的: 构建一个 V 个点的有向图 G,初始为没有任何边,接下来羽月在脑中构建出一个长度为 E 的边的序列,序列中元素两两不同,然后羽月将这些边依次加入图中,每次加入之后计算当前图的强连通分量个数并记下来,最后得到一个长度为E 的序列,这个序列就是能力的效果. 注意到,可能存在边的序列不同而能力效果相同的情况,所以羽月想请你帮她计算能发动的不同能力个数,答案对 998244353 取模.你需要对于1<=E<=V*(V-1)的所有 E 计

[jzoj 6084] [GDOI2019模拟2019.3.25] 礼物 [luogu 4916] 魔力环 解题报告(莫比乌斯反演+生成函数)

题目链接: https://jzoj.net/senior/#main/show/6084 https://www.luogu.org/problemnew/show/P4916 题目: 题解: 我们设$f(x)$表示最小循环节长度为x的合法序列数,那么有$ans=\sum_{d|gcd(n,m)}\frac{1}{d}f(d)$ 这是因为最小循环节为d的序列对应的环会被计算d次,比如 0101,最小循环节长度为 2(循环节为 01),其对应的环会被统计 2 次(序列 0101 与 1010)