• 洛谷P4517 [JSOI2018]防御网络(dp)


    题面

    传送门

    题解

    翻译一下题意就是每次选出一些点,要用最少的边把这些点连起来,求期望边数

    我也不知道为什么反正总之就是暴力枚举太麻烦了所以我们考虑贡献

    如果一条边是割边,那么它会在图里当且仅当两边的联通块中都有点被选,设其中一边的点的个数为(siz),那么方案数就是((2^{siz}-1)(2^{n-siz}-1))

    然而如果一条边是环边就会变得非常麻烦了……每种方案相当于这个环上有若干个点被标记,要用最少的边数将它们连起来,那么边数就是环的大小减去(max()两个相邻点之间的边数())

    这个不能分开考虑了,我们把整个环给一起考虑

    (dp_{i,j,k})表示选择的点中左端点为(i),右端点为(j),不考虑(j->n->i)这条路径的情况下的两两相邻点之间最大值为(k)的情况,设环长为(n),那么这个就会产生((n-max(k,n+i-j)) imes dp_{i,j,k})的贡献

    转移方程为

    [dp_{i,j,k}=(2^{siz_j}-1)left(sum_{p=0}^kdp_{i,j-k,p}+sum_{p=j-k+1}^{j-1}dp_{i,p,k} ight) ]

    上面的方程的意思就是,如果(j)和前面一个点的长度恰好为(k),那么前面最大长度可以任取,否则前面最大长度就必须等于(k)

    发现转移方程里的两个柿子都可以写成前缀和的形式,那么我们一边计算一边维护前缀和,总的复杂度就是(O(n^3))

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #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)
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    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;
    }
    const int N=1005,P=1e9+7;
    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;
    }
    struct eg{int v,nx,w,f;}e[N<<1];int head[N],tot=1;
    inline void add_edge(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
    int dfn[N],low[N],sz[N],vis[N],sum1[N][N],sum2[N][N],st[N],val[N];
    int n,m,tim,res,top,tmp,u,v;
    void tarjan(int u,int fa){
    	low[u]=dfn[u]=++tim,sz[u]=1;
    	go(u)if(v!=fa){
    		if(!dfn[v])tarjan(v,u),cmin(low[u],low[v]),sz[u]+=sz[v];
    		else cmin(low[u],dfn[v]);
    		if(low[v]>dfn[u]){
    			e[i].f=e[i^1].f=1,res=add(res,mul(ksm(2,sz[v])-1,ksm(2,n-sz[v])-1));
    			e[i].w=sz[v],e[i^1].w=n-sz[v];
    		}
    	}
    }
    inline int nxt(R int u){go(u)if(!e[i].f&&!vis[v])return v;return 0;}
    void solve(int p){
    	top=0;while(p)st[++top]=p,vis[p]=1,p=nxt(p);
    	if(top==1)return;
    	fp(i,1,top)val[i]=1;
    	fp(u,1,top)go(st[u])val[u]+=e[i].w;
    	fp(i,1,top)val[i]=ksm(2,val[i])-1;
    	fp(i,1,top-1){
    		fp(j,0,top)sum2[i][j]=val[i];
    		fp(j,i+1,top)fp(k,1,top){
    			tmp=0;
    			if(j-i>=k){
    				tmp=mul(val[j],add(sum2[j-k][k],dec(sum1[j-1][k],sum1[j-k][k])));
    				res=add(res,mul(tmp,top-max(top+i-j,k)));
    			}
    			sum1[j][k]=add(sum1[j-1][k],tmp),sum2[j][k]=add(sum2[j][k-1],tmp);
    		}
    		fp(j,i,top)fp(k,0,top)sum1[j][k]=sum2[j][k]=0;
    	}
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	n=read(),m=read();
    	fp(i,1,m)u=read(),v=read(),add_edge(u,v),add_edge(v,u);
    	tarjan(1,0);
    	fp(i,1,n)if(!vis[i])solve(i);
    	printf("%d
    ",mul(res,ksm(ksm(2,n),P-2)));
    	return 0;
    }
    
  • 相关阅读:
    Constants and Variables
    随想
    C#基础篇之语言和框架介绍
    Python基础19 实例方法 类方法 静态方法 私有变量 私有方法 属性
    Python基础18 实例变量 类变量 构造方法
    Python基础17 嵌套函数 函数类型和Lambda表达式 三大基础函数 filter() map() reduce()
    Python基础16 函数返回值 作用区域 生成器
    Python基础11 List插入,删除,替换和其他常用方法 insert() remove() pop() reverse() copy() clear() index() count()
    Python基础15 函数的定义 使用关键字参数调用 参数默认值 可变参数
    Python基础14 字典的创建修改访问和遍历 popitem() keys() values() items()
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10512110.html
Copyright © 2020-2023  润新知