• JSOI2018 防御网络


    一道很有思维难度的(dp)和仙人掌(雾
    (LOJ)
    (Luogu)


    题目描述

    题目要求一个(n)个点的点仙人掌的所有点集的斯坦纳树的边数的期望
    (nleq 200)
    如果是一般图,这个问题是只能大力状压的.
    但是这个图是一棵点仙人掌,那样就有多项式复杂度的算法了.


    解析

    由期望的线性性得我们可以把所有的边分开计算.
    对于一条边((u,v)),我们分两种情况讨论.

    情况(1):((u,v))是桥

    只要我们选中了桥两边的点,那么((u,v))必被选,否则((u,v))必不被选.
    因此我们在(tarjan)的过程中维护一个(size),就直接把桥的贡献算掉了.

    [Ans=(2^{size_v}-1)*(2^{n-size_v}-1) ]

    情况(2):((u,v))是环边

    这下情况变得复杂了.
    我们想要算((u,v))的贡献,发现十分棘手.
    比如一个六元环,按顺序标号为(1,2,3,4,5,6)
    我们选的点集在点(1,3,5)的儿子中.
    那么这个环上所有边都有可能有贡献.
    这样,想要算单独一条边的贡献就算不出来了.
    我们考虑如何计算整个环的贡献.
    我们称选了一个点是指选了它的儿子中的点.
    在环上可能选了许多点.相邻的两个点中有距离.我们肯定是选择环长-最长距离作为答案.
    (F_{i,j,k})表示选的最左边点是(i),最右边点是(j),不考虑(i,j)之间路径的情况下答案是(k)的点集方案数.
    那么(F_{i,j,k})的贡献就是(F_{i,j,k}*max(k,len-j+i))
    再令(w_i)表示环上第(i)个点,删除所有和它相连的环边之后,它所在的连通块的大小.
    那么,我们如何转移(F)值呢?
    当然就是暴力枚举上一个(j)
    那么(dp)方程就写出来了
    (F_{i,j,k}=(2^{w_j}-1)*(sum_{t=0}^kF_{i,j-k,t}+sum_{t=j-k+1}^{j-1}F_{i,t,k}))
    前半个(sum)是计算如果(j)和上一个点的距离等于(k),这时上一个点的最大值可以随便取.后半部分是(j)和上一个点的距离不等于(k),这时上一个点的最大值只能为(k).
    那么我们发现这时一个前缀和的形式,那么只要二维前缀和一下即可.
    完结撒花!

    代码如下(经过了小幅度压行)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #define N (210)
    #define M (80010)
    #define P (1000000007)
    #define inf (0x7f7f7f7f)
    #define rg register int
    #define Label puts("NAIVE")
    #define spa print(' ')
    #define ent print('
    ')
    #define rand() (((rand())<<(15))^(rand()))
    typedef long double ld;
    typedef long long LL;
    typedef unsigned long long ull;
    using namespace std;
    inline char read(){
    	static const int IN_LEN=1000000;
    	static char buf[IN_LEN],*s,*t;
    	return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
    }
    template<class T>
    inline void read(T &x){
    	static bool iosig;
    	static char c;
    	for(iosig=false,c=read();!isdigit(c);c=read()){
    		if(c=='-')iosig=true;
    		if(c==-1)return;
    	}
    	for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^'0');
    	if(iosig)x=-x;
    }
    inline char readchar(){
    	static char c;
    	for(c=read();!isalpha(c);c=read())
    	if(c==-1)return 0;
    	return c;
    }
    const int OUT_LEN = 10000000;
    char obuf[OUT_LEN],*ooh=obuf;
    inline void print(char c) {
    	if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf;
    	*ooh++=c;
    }
    template<class T>
    inline void print(T x){
    	static int buf[30],cnt;
    	if(x==0)print('0');
    	else{
    		if(x<0)print('-'),x=-x;
    		for(cnt=0;x;x/=10)buf[++cnt]=x%10+48;
    		while(cnt)print((char)buf[cnt--]);
    	}
    }
    inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);}
    int ne[M],fi[N],b[M],siz[N],n,m,E=1;
    int dfn[N],low[N],ind,cir[N],sz[M];
    LL ans,S1[N][N],S2[N][N],w[N]; bool vis[N];
    LL ksm(LL a,int p){
    	LL res=1;
    	while(p){
    		if(p&1)res=(res*a)%P;
    		a=(a*a)%P,p>>=1;
    	}
    	return res;
    }
    void add(int x,int y){ne[++E]=fi[x],fi[x]=E,b[E]=y;}
    int nxt(int u){
    	for(int i=fi[u];i;i=ne[i]){
    		int v=b[i];
    		if(low[v]>dfn[u]||low[u]>dfn[v]||vis[v])continue;
    		return v;
    	}
    	return 0;
    }
    void dp(int st){
    	int cnt=0;
    	for(int i=st;i;i=nxt(i))cir[++cnt]=i,vis[i]=1;
    	if(cnt==1)return;
    	for(int i=1;i<=cnt;i++){
    		int t=cir[i];
    		for(int j=fi[t];j;j=ne[j])
    		w[t]+=sz[j];w[t]=(ksm(2ll,w[t]+1)-1ll);
    	}
    	for(int i=1;i<cnt;i++){
    		for(int j=0;j<=cnt;j++)S1[i][j]=w[cir[i]];
    		for(int j=i+1;j<=cnt;j++){
    			for(int k=1;k<=j-i;k++){
    				LL f=(S1[j-k][k]+S2[j-1][k]-S2[j-k][k]+P)%P*w[cir[j]]%P;
    	            (ans+=f*(cnt-max(cnt-j+i,k)))%=P;
    	            S1[j][k]=(S1[j][k-1]+f)%P,S2[j][k]=(S2[j-1][k]+f)%P;
    			}
    			for(int k=j-i+1;k<=cnt;k++)
    			S1[j][k]=S1[j][k-1],S2[j][k]=S2[j-1][k];
    		}
    	}
    	for(int i=0;i<=cnt;i++)
    	for(int j=0;j<=cnt;j++)
    	S1[i][j]=S2[i][j]=0;
    }
    void tarjan(int u,int pre){
    	dfn[u]=low[u]=++ind,siz[u]=1;
    	for(int i=fi[u];i;i=ne[i]){
    		int v=b[i];
    		if(!dfn[v]){
    			tarjan(v,u),siz[u]+=siz[v];
    			low[u]=min(low[v],low[u]);
    			if(low[v]>dfn[u])
    				sz[i]=siz[v],sz[i^1]=n-siz[v],
    				ans=(ans+(ksm(2ll,siz[v])-1ll)*(ksm(2ll,n-siz[v])-1ll)%P)%P;
    		}
    		else if(v!=pre)low[u]=min(low[u],dfn[v]);
    	}
    }
    int main(){
    	read(n),read(m);
    	for(int i=1,x,y;i<=m;i++)
    	read(x),read(y),add(x,y),add(y,x);
    	tarjan(1,0);for(int i=1;i<=n;i++)if(!vis[i])dp(i);
    	printf("%lld
    ",(ans*ksm(ksm(2,n),P-2))%P);
    }
    
  • 相关阅读:
    5-1 Leetcode中和链表相关的问题
    4-7 带有尾指针的链表:使用链表实现队列
    4.6 使用链表实现栈
    4.5 链表元素的删除
    4.4 链表的遍历、查询和修改
    4.3 为链表设置虚拟头结点dummyhead
    4.2在链表中添加元素
    4.1链表
    mybatis 力量操作参数为List的非空校验
    linux 运行和停止jar的shell 脚本
  • 原文地址:https://www.cnblogs.com/Romeolong/p/10108972.html
Copyright © 2020-2023  润新知