zoukankan      html  css  js  c++  java
  • BZOJ3451 Normal 和 CF235D Graph Game

    Normal

    定义一次点分治的复杂度是所有分治中心分治时的子树大小之和。

    给定一棵树,问所有点等概率被选做重心,点分治的期望复杂度。

    (n leq 30000)

    题解

    https://www.cnblogs.com/SGCollin/p/10597925.html

    根据期望的线性性,答案等价于每个点在点分树上的深度期望之和。

    思路是从点对的角度考虑某一个点是否会产生贡献。

    [E(dep_x)=sum_{y=1}^n P(xin subtree_y) ]

    也就是 (x) 在点分树上在 (1dots n) 的子树中的概率和。

    考虑点分树上 (y)(x) 的祖先的条件,要求 (x)(y) 构成的这条链上第一个在点分治过程中被删除的点是 (y) ,由于链上被选中的概率相等,因此这个概率为 (frac{1}{dis(x,y) + 1})

    所以答案为

    [sum_{x=1}^nsum_{j=1}^n frac{1}{dis(i,j) + 1}\ =sum_{i = 0}^n frac{cnt_i}{i + 1} ]

    因此需要点分治求长度为 (i) 的路径条数 (cnt_i) ,注意到合并的时候是卷积的形式。

    容斥做法

    不考虑重复路径,把子树DFS一遍,直接自己和自己进行卷积,再去掉子树内重复计数的路径即可。

    每一层最差以自己的size作为长度进行卷积,因此复杂度为 (O(nlog^2 n))

    CO int N=1<<16;
    int omg[2][N],rev[N];
    
    void NTT(int a[],int lim,int dir){
    	int len=log2(lim);
    	for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
    	for(int i=0;i<lim;++i)if(i<rev[i]) swap(a[i],a[rev[i]]);
    	for(int i=1;i<lim;i<<=1)
    		for(int j=0;j<lim;j+=i<<1)
    			for(int k=0;k<i;++k){
    				int t=mul(omg[dir][N/(i<<1)*k],a[j+i+k]);
    				a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t);
    			}
    	if(dir==1){
    		int ilim=fpow(lim,mod-2);
    		for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim);
    	}
    }
    
    vector<int> to[N];
    bool vis[N];
    int siz[N],all;
    pair<int,int> root;
    
    void find_root(int u,int fa){
    	siz[u]=1;
    	int big=0;
    	for(int v:to[u])if(v!=fa and !vis[v]){
    		find_root(v,u);
    		siz[u]+=siz[v],big=max(big,siz[v]);
    	}
    	big=max(big,all-siz[u]);
    	root=min(root,make_pair(big,u));
    }
    
    int buc[N],len,cnt[N];
    
    void dfs(int u,int fa,int dep){
    	++buc[dep],len=max(len,dep);
    	for(int v:to[u])if(v!=fa and !vis[v]) dfs(v,u,dep+1);
    }
    void calc(int u,int o){
    	len=0,dfs(u,0,0);
    	int lim=1<<int(ceil(log2(2*len+1)));
    	NTT(buc,lim,0);
    	for(int i=0;i<lim;++i) buc[i]=mul(buc[i],buc[i]);
    	NTT(buc,lim,1);
    	if(o>0){
    		for(int i=0;i<=2*len;++i) cnt[i]+=buc[i];
    	}
    	else{
    		for(int i=0;i<=2*len;++i) cnt[i+2]-=buc[i];
    	}
    	memset(buc,0,lim*sizeof(int));
    }
    
    void divide(int u){
    	vis[u]=1;
    	calc(u,1);
    	int oall=all;
    	for(int v:to[u])if(!vis[v]){
    		calc(v,-1);
    		all=siz[v]<siz[u]?siz[v]:oall-siz[u],root=make_pair(all,0);
    		find_root(v,0),divide(root.second);
    	}
    }
    
    int main(){
    	omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N);
    	omg[1][0]=1,omg[1][1]=fpow(332748118,(mod-1)/N);
    	for(int i=2;i<N;++i){
    		omg[0][i]=mul(omg[0][i-1],omg[0][1]);
    		omg[1][i]=mul(omg[1][i-1],omg[1][1]);
    	}
    	int n=read<int>();
    	for(int i=1;i<n;++i){
    		int u=read<int>()+1,v=read<int>()+1;
    		to[u].push_back(v),to[v].push_back(u);
    	}
    	all=n,root=make_pair(all,0);
    	find_root(1,0),divide(root.second);
    	LD ans=0;
    	for(int i=0;i<=n;++i) ans+=(LD)cnt[i]/(i+1);
    	printf("%.4Lf
    ",ans);
    	return 0;
    }
    

    看了别人的代码,才发现之前自己的预处理原根是多么智障。

    Graph Game

    给你⼀个 (n) 个点,(n) 条边的连通图。每次随机选⼀个点删除,然后递归所有连通分量,代价是每次连通块⼤⼩。

    问代价期望。(3 ≤ n ≤ 3000)

    题解

    https://www.cnblogs.com/yzxverygood/p/10406215.html

    也就是需要对两点之间的路径经过环的概率容斥一下。

    CO int N=3e3+10;
    vector<int> to[N];
    int deg[N],vis[N],cir[N],len;
    int top[N],fa[N][12],dep[N];
    
    void dfs(int u){
    	cir[++len]=u,vis[u]=len;
    	for(int v:to[u])if(!vis[v]) dfs(v);
    }
    int lca(int x,int y){
    	if(dep[x]<dep[y]) swap(x,y);
    	for(int i=11;i>=0;--i)if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    	if(x==y) return x;
    	for(int i=11;i>=0;--i)if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    	return fa[x][0];
    }
    int main(){
    	int n=read<int>();
    	for(int i=1;i<=n;++i){
    		int u=read<int>()+1,v=read<int>()+1;
    		to[u].push_back(v),to[v].push_back(u);
    		++deg[u],++deg[v];
    	}
    	deque<int> Q;
    	for(int u=1;u<=n;++u)if(deg[u]==1) Q.push_back(u);
    	while(Q.size()){
    		int u=Q.front();Q.pop_front();
    		vis[u]=-1;
    		for(int v:to[u])if(--deg[v]==1) Q.push_back(v);
    	}
    	for(int u=1;u<=n;++u)if(deg[u]==2){
    		dfs(u);break;
    	}
    	for(int u=1;u<=n;++u)if(vis[u]>0){
    		top[u]=u,dep[u]=1;
    		Q.push_back(u);
    		while(Q.size()){
    			int u=Q.front();Q.pop_front();
    			for(int i=1;i<=11;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
    			for(int v:to[u])if(vis[v]==-1 and v!=fa[u][0]){
    				top[v]=top[u],fa[v][0]=u,dep[v]=dep[u]+1;
    				Q.push_back(v);
    			}
    		}
    	}
    	LD ans=0;
    	for(int u=1;u<=n;++u)for(int v=1;v<=n;++v){
    		if(top[u]==top[v]){
    			int f=lca(u,v);
    			ans+=(LD)1/(dep[u]+dep[v]-2*dep[f]+1);
    		}
    		else{
    			int x=dep[u]-dep[top[u]]+1+dep[v]-dep[top[v]]+1;
    			int y=abs(vis[top[u]]-vis[top[v]])-1,z=len-2-y;
    			ans+=(LD)1/(x+y)+(LD)1/(x+z)-(LD)1/(x+y+z);
    		}
    	}
    	printf("%.9Lf
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    使用CSS3美化复选框checkbox
    MYSQL select时锁定记录问题
    SQL Server中的锁
    T-SQL查询进阶—理解SQL Server中的锁
    SQL:查找被锁的表,以及锁表的SQL语句(重点推荐)
    Mac 常用的手势
    Mac 常用的快捷键
    Mac上的学习神器:Marginnote
    Mac 常用命令介绍
    Mac 上的传奇效率神器 Alfred 3
  • 原文地址:https://www.cnblogs.com/autoint/p/12107764.html
Copyright © 2011-2022 走看看