zoukankan      html  css  js  c++  java
  • LOJ #2547 Luogu P4517「JSOI2018」防御网络

    好像也没那么难写

    LOJ #2547

    Luogu P4517


    题意

    在一棵点仙人掌中等概率选择一个点集

    求选出点集的斯坦纳树大小的期望

    定义点仙人掌为不存在一个点在多个简单环中的连通图

    斯坦纳树为在原图中连通给定点集的一棵生成树

    点数不超过$ 200$


    $ Solution$

    直接计算不太方便

    我们转而考虑每条边的贡献

    如果这条边不在环上则一定是割边

    若这条边两边都有点被选择就会被计算贡献

    如果这条边在环上比较复杂

    对于一个环,我们选择的边的数量一定是环大小-最长没选中点的路径的长度

    定义选中某个点为存在至少一个点满足这个点到环的最近点为这个点

    用$ f(L,R,k,0/1)$表示将环展开成链之后选中的左右端点为$ L,R$,这之间的最长空路径长度为$ k$且已经/没有取到最长路径的方案数

    转移可以用前缀和优化$ O(n^3)$计算出$ DP$值

    然后对于一个$ f(L,R,k,1)$最长空路径长度为$ max(k,n+L-R)$然后统计答案

    总复杂度为$ O(n^3)$


    $ my code$

    #include<ctime>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #define p 1000000007
    #define rt register int
    #define ll long long
    using namespace std;
    inline ll read(){
        ll x = 0; char zf = 1; char ch = getchar();
        while (ch != '-' && !isdigit(ch)) ch = getchar();
        if (ch == '-') zf = -1, ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
    }
    void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
    void writeln(const ll y){write(y);putchar('
    ');}
    int k,m,n,x,y,z,cnt,ans;
    int e[205][205];
    int f[205][2],qz0[205],qz1[205],jc[205];
    void dp(int n,int *A){
        if(n<=2)return;
        for(rt i=1;i<=n;i++)
        for(rt k=1;k<=n;k++){
            memset(qz0,0,sizeof(qz0));
            memset(qz1,0,sizeof(qz1));
            qz0[i]=A[i];f[i][0]=A[i];
            for(rt j=i+1;j<=n;j++){
                (f[j][0]=1ll*A[j]*(qz0[j-1]-qz0[max(j-k,i-1)])%p)%=p,
                (f[j][1]=1ll*A[j]*(qz1[j-1]-qz1[max(j-k,i-1)])%p)%=p;
                    
                if(j-k>=i)(f[j][1]+=(1ll*A[j]*(f[j-k][0]+f[j-k][1])%p)%p)%=p;
                int len=max(k,n-j+i); 
                (cnt+=1ll*f[j][1]*(n-len)%p)%=p;
                    
                qz1[j]=(qz1[j-1]+f[j][1])%p;
                qz0[j]=(qz0[j-1]+f[j][0])%p;
            }
        }     
    }
    int a[205],dfn[205],low[205],sta[205],s[205],sl,top,tot;
    bool vis[205];
    int js(int x){
        int sum=1;vis[x]=1;
        for(rt i=1;i<=n;i++)if(!vis[i]&&e[x][i])(sum+=js(i))%=p;
        return sum;
    }
    void calc(int n,int *s){
        if(!n)return;
        memset(vis,0,sizeof(vis));
        for(rt i=1;i<=n;i++)vis[s[i]]=1;
        for(rt i=1;i<=n;i++)a[i]=jc[js(s[i])]-1;
        dp(n,a);
    }
    void dfs(int x,int pre){
        dfn[x]=low[x]=++tot;sta[++top]=x;
        for(rt i=1;i<=n;i++)if(e[x][i]&&x!=i&&pre!=i){
            if(!dfn[i]){
                dfs(i,x);
                low[x]=min(low[x],low[i]);
                if(low[i]>dfn[x]){
                    sl=0;while(sta[top+1]!=i)s[++sl]=sta[top--];
                    calc(sl,s);memset(vis,0,sizeof(vis));
                    vis[x]=1;int gs=js(i);
                    (cnt+=1ll*(jc[gs]-1)*(jc[n-gs]-1)%p)%=p;
                }
            }
            else if(i!=pre)low[x]=min(low[x],dfn[i]);
        }
        if(x==1){
            sl=0;while(top)s[++sl]=sta[top--];
            calc(sl,s);
        }
    }
    int inv(int x){return (x==1)?1:1ll*inv(p%x)*(p-p/x)%p;}
    int main(){
        n=read();m=read();
        jc[0]=1;
        for(rt i=1;i<=n;i++)jc[i]=1ll*jc[i-1]*2%p;
        for(rt i=1;i<=m;i++){
            x=read();y=read();
            e[x][y]=e[y][x]=1;
        }
        dfs(1,1);
        cout<<(1ll*cnt*inv(jc[n])%p+p)%p;
        return 0;
    }
  • 相关阅读:
    字符串截取例子 大小写转换 拼接例子 把oc转成c语言
    字符串比较 是否相同 大小 创建字符串 例子
    数组于字典:把多个字典放到数组中
    考核7
    Delphi 单元不能被循环引用
    Delphi ADOQuery无法更新定位行问题(其它数据库访问控件类似)
    SQL远程服务器操作数据
    s​p​c​o​m​m​属​性​详​解
    dxSkinController动态调入皮肤
    RegisterClass与GetClass
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/10098962.html
Copyright © 2011-2022 走看看