zoukankan      html  css  js  c++  java
  • jzoj5987. 【WC2019模拟2019.1.4】仙人掌毒题 (树链剖分+概率期望+容斥)

    题面

    题解

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

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

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

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

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

    (0)点为白点,(1)点为黑点,那么对于一个点,它在染完色之后仍为白色的概率为((frac{n-1}{n})^t),那么白点的期望个数就是(n imes (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}{ichoose j}),就是枚举有多少个黑点并容斥,可以用多项式求逆解决

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

    总的时间复杂度为(O(nlog t+mlog^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]='
    ';
    }
    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;
    }
    
  • 相关阅读:
    视图和同义词的区别
    【MooTools】自定义滚动条小插件
    有理想的程序员必须知道的15件事
    革新:.NET 2.0的自定义配置文件体系初探
    我的2006年学习计划
    为ASP.NET 2.0配置数据源
    通用异常处理框架
    泛型的序列化问题
    实战SVN For Apache2(二)
    LightningFramework系列(一、初步总架构图)
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10223695.html
Copyright © 2011-2022 走看看