zoukankan      html  css  js  c++  java
  • BZOJ5315 [JSOI2018]防御网络 【仙人掌 + dp】

    题目链接

    BZOJ5315

    题解

    题目好吓人= =点仙人掌 + 斯坦纳树

    我们只需求出对于所有选点的方案的斯坦纳树边长总和
    (n)那么大当然不能状压,但是考虑一下如果这是一棵树,一个方案的贡献就是连接这些点的所有边
    我们可以考虑计算每条边的贡献
    一条边在树上有贡献,当且仅当它两端的树都存在被选择的点
    那么这条边((u,v))贡献就是

    [(2^{siz[u]} - 1)(2^{siz[v] - 1}) ]

    其中(siz[u])表示断开这条边后(u)一侧的树大小

    如果放到仙人掌上呢?
    对于割边,和树是一样的
    我们只需计算每个环的贡献
    考虑我们对于一个环,选择了其中(K)个点所在外向树,那么就有连接(K)个点的环上的(K)段边,我们一定是除去最长那一条
    所以我们断环为链,设(f[i][j][k])为选择了区间([i,j])的外向树【意味着端点必选,中间不一定选,区间外一定不选】,([i,j])中最大距离为(k)的方案数
    那么有,即考虑最后一段的长度

    [f[i][j][k] = (2^{siz[j]} - 1)(sumlimits_{x = 0}^{k}f[i][j - k][x] + sumlimits_{x = j - k + 1}^{j - 1}f[i][x][k]) ]

    直接转移是(O(n^4))的,常数很小数据很水可以跑过。。。

    当然可以前缀和优化成(O(n^3))
    【其实是我前缀和写炸了,直接交一波暴力转移竟然(A)了。。。】

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    using namespace std;
    const int maxn = 205,maxm = 100005,INF = 1000000000,P = 1000000007;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    int h[maxn],ne = 1;
    struct EDGE{int to,nxt;}ed[maxm];
    inline void build(int u,int v){
    	ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
    	ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
    }
    int n,m;
    inline int qpow(int a,int b){
    	int re = 1;
    	for (; b; b >>= 1,a = 1ll * a * a % P)
    		if (b & 1) re = 1ll * re * a % P;
    	return re;
    }
    int dfn[maxn],low[maxn],siz[maxn],fa[maxn],cnt,sum;
    int v[maxn],K;
    LL f[maxn][maxn][maxn],D[maxn][maxn][maxn],S[maxn][maxn][maxn],ans,bin[maxn];
    void DP(int rt,int u){
    	K = 0; int tot = 0;
    	for (int i = u; i != rt; i = fa[i]){
    		v[++K] = siz[i];
    		siz[rt] += siz[i];
    		tot += siz[i];
    	}
    	v[++K] = sum - tot;
    	cls(f);
    	for (int l = 1; l <= K; l++)
    		for (int r = l; r <= K; r++)
    			for (int k = 0; k <= r - l; k++){
    				if (l == r){
    					if (k == 0) f[l][r][k] = bin[v[l]] - 1;
    					continue;
    				}
    				int d = 0,s = 0;
    				for (int i = 0; i <= k; i++) d = (d + f[l][r - k][i]) % P;
    				for (int i = r - k + 1; i < r; i++) s = (s + f[l][i][k]) % P;
    				f[l][r][k] = (bin[v[r]] - 1) * (d + s) % P;
    				ans = (ans + 1ll * (K - max(K - r + l,k)) * f[l][r][k] % P) % P;
    			}
    }
    void dfs(int u){
    	dfn[u] = low[u] = ++cnt; siz[u] = 1;
    	Redge(u) if ((to = ed[k].to) != fa[u]){
    		if (!dfn[to]){
    			fa[to] = u; dfs(to);
    			low[u] = min(low[u],low[to]);
    		}
    		else low[u] = min(low[u],dfn[to]);
    		if (low[to] > dfn[u]){
    			ans = (ans + 1ll * (bin[siz[to]] - 1) * (bin[sum - siz[to]] - 1) % P) % P;
    			siz[u] += siz[to];
    		}
    	}
    	Redge(u) if (fa[to = ed[k].to] != u && dfn[u] < dfn[to])
    		DP(u,to);
    }
    int main(){
    	bin[0] = 1; for (int i = 1; i <= 200; i++) bin[i] = bin[i - 1] * 2ll % P;
    	n = read(); m = read();
    	while (m--) build(read(),read());
    	sum = n; dfs(1);
    	ans = ans * qpow(bin[n],P - 2) % P;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    浅谈mongodb内存
    iptables 【转载】
    腾讯公司运维面试题
    nginx 的一些优化(突破十万并发)
    nginx 查看 并发连接数
    Linux内核调优部分参数说明
    web性能测试基本性能指标
    sort
    sed笔记
    ubuntu 18.04安装腾讯通RTX2015
  • 原文地址:https://www.cnblogs.com/Mychael/p/9197507.html
Copyright © 2011-2022 走看看