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('\n');}
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;
}

 

posted @ 2018-12-10 20:40  Kananix  阅读(215)  评论(0编辑  收藏  举报

Contact with me