题目

神仙题。

首先我们可以把题意转化为图的独立集计数。显然这个东西是个NP-Hard的。

然后我们可以注意到\(m\le n+10\),也就是说最多有\(11\)条非树边。

我们现在先考虑一下,树上独立集计数怎么做。

设\(f_{u,0/1}\)表示\(u\)点选/不选的方案数。

那么转移方程就是:

\(f_{u,0}=\prod\limits_{v\in son_u}(f_{v,0}+f_{v,1})\)

\(f_{u,1}=\prod\limits_{v\in son_u}f_{v,0}\)

最后的答案就是\(f_{1,0}+f_{1,1}\)。

然后我们知道现在有最多\(11\)条非树边,我们可以枚举每条非树边两端的状态,然后再跑一遍dp。

这样每条非树边有\((0,0),(0,1),(1,0)\)三种情况。可以发现\((0,0),(0,1)\)这两种情况可以合并。这样就变成枚举每条非树边的起点选不选。

所以我们就得到了一个\(O(n2^{m-n+1})\)的优秀做法。根据常数可以获得\(70\sim80\)分的好成绩。

然后我们来想一件事情,我们每次做dp的时候,有很多转移是浪费的。

我们可以把每个点的\(f\)看做一个向量,那么转移就可以看做是一个矩阵。

然后我们会发现转移基本上都是不变的,变的只是我们枚举强制选不选的那些点的向量。

这启发我们用虚树来处理dp部分。

把枚举强制选不选的那些点看做是关键点,然后对于它们建一棵虚树,然后dp。

所以现在我们需要求的就是虚树上的一条边(对应原树上的一条链)的转移的系数,这个东西我们可以通过在原树上自下而上dfs一遍求得。这个过程可能有点麻烦。

实际上建虚树的过程也可以在上面这个dfs的过程中来完成。

然后我们就可以在虚树上面跑dp了,时间复杂度为\(O(n+(m-n+1)2^{m-n+1})\)。

#include<bits/stdc++.h>
#define pb push_back
#define pi pair<int,int>
#define fi first
#define se second
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],*iS,*iT;
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
}using namespace IO;
const int N=100020,P=998244353;
int inc(int a,int b){return a+=b,a>=P? a-P:a;}
int mul(int a,int b){return 1ll*a*b%P;}
int n,m;
namespace Graph
{
vector<int>E[N];int T,cnt,dfn[N],size[N],vis[N],fa[N];
struct edge{int u,v;}e[N];
void add(int u,int v){E[u].pb(v),E[v].pb(u);}
void dfs(int u)
{
dfn[u]=++T;
for(int v:E[u])
if(v^fa[u])
if(!dfn[v]) fa[v]=u,dfs(v),size[u]+=size[v];
else if(dfn[v]<dfn[u]) e[++cnt]={u,v},vis[u]=vis[v]=1;
vis[u]|=size[u]>=2,size[u]=size[u]||vis[u];
}
}using namespace Graph;
namespace ITree
{
int f[N][2],g[N][2],p[N][2],is[N];pi k[N][2];
pi operator+(pi a,pi b){return pi(inc(a.fi,b.fi),inc(a.se,b.se));}
pi operator*(pi a,int k){return pi(mul(k,a.fi),mul(k,a.se));}
struct node{int v;pi a,b;};vector<node>G[N];
void Add(int u,int v,pi a,pi b){G[u].pb({v,a,b});}
int build(int u)
{
p[u][0]=p[u][1]=1,is[u]=1;int pos=0,w;
for(int v:E[u])
if(!is[v])
{
w=build(v);
if(!w) p[u][1]=mul(p[u][1],p[v][0]),p[u][0]=mul(p[u][0],inc(p[v][0],p[v][1]));
else if(vis[u]) Add(u,w,k[v][0]+k[v][1],k[v][0]);
else k[u][1]=k[v][0],k[u][0]=k[v][0]+k[v][1],pos=w;
}
if(vis[u]) k[u][0]=pi(1,0),k[u][1]=pi(0,1),pos=u; else k[u][0]=k[u][0]*p[u][0],k[u][1]=k[u][1]*p[u][1];
return pos;
}
void dp(int u)
{
(f[u][0]=g[u][1]? 0:p[u][0]),(f[u][1]=g[u][0]? 0:p[u][1]);
for(auto [v,a,b]:G[u])
{
dp(v);int p=f[v][0],q=f[v][1];
f[u][0]=mul(f[u][0],inc(mul(a.fi,p),mul(a.se,q)));
f[u][1]=mul(f[u][1],inc(mul(b.fi,p),mul(b.se,q)));
}
}
}using namespace ITree;
int main()
{
n=read(),m=read();int ans=0;
for(int i=1,u,v;i<=m;++i) u=read(),v=read(),add(u,v);
dfs(1),vis[1]=1,build(1);
for(int S=0,i;S<1<<cnt;++S)
{
for(i=1;i<=cnt;++i) if(S>>(i-1)&1) g[e[i].u][1]=g[e[i].v][0]=1; else g[e[i].u][0]=1;
dp(1),ans=inc(ans,inc(f[1][1],f[1][0]));
for(i=1;i<=cnt;++i) if(S>>(i-1)&1) g[e[i].u][1]=g[e[i].v][0]=0; else g[e[i].u][0]=0;
}
printf("%d",ans);
}

Luogu P4426 [HNOI/AHOI2018]毒瘤的更多相关文章

  1. P4426 [HNOI/AHOI2018]毒瘤

    挺不错的一个题. 题意即为求一个图的独立集方案数. 如果原图是一棵树,可以直接大力f[x][0/1]来dp. 由于非树边很少,考虑2^11容斥,强制某些点必选,然后再O(n)dp,这样应该过不了. 发 ...

  2. 洛谷 P4426 - [HNOI/AHOI2018]毒瘤(虚树+dp)

    题面传送门 神仙虚树题. 首先考虑最 trival 的情况:\(m=n-1\),也就是一棵树的情况.这个我相信刚学树形 \(dp\) 的都能够秒掉罢(确信).直接设 \(dp_{i,0/1}\) 在表 ...

  3. 【题解】Luogu P4436 [HNOI/AHOI2018]游戏

    原题传送门 \(n^2\)过百万在HNOI/AHOI2018中真的成功了qwqwq 先将没门分格的地方连起来,枚举每一个块,看向左向右最多能走多远,最坏复杂度\(O(n^2)\),但出题人竟然没卡(建 ...

  4. luogu P4437 [HNOI/AHOI2018]排列

    luogu 问题本质是把\(a_i\)作为\(i\)的父亲,然后如果有环就不合法,否则每次要取数,要满足取之前他的父亲都被取过(父亲为0可以直接取),求最大价值 贪心想法显然是要把权值大的尽量放在后面 ...

  5. [HNOI/AHOI2018]毒瘤

    题目描述 https://www.lydsy.com/JudgeOnline/upload/201804/%E6%B9%96%E5%8D%97%E4%B8%80%E8%AF%95%E8%AF%95%E ...

  6. 【题解】Luogu P4438 [HNOI/AHOI2018]道路

    原题传送门 实际就是一道简单的树形dp 设f[u][i][j]表示从根结点到结点u经过i条未翻修公路,j条未翻修铁路的贡献最小值 边界条件:f[leaf][i][j]=(A+i)(B+j)C (题目上 ...

  7. #10 //I [HNOI/AHOI2018]毒瘤

    题解: 80分做法还是听简单的 对于非树边枚举一下端点状态 然而我也不知道为什么就多t了一个点 具体实现上 最暴力的是3^n次 但是我们可以发现对于i不取,j取 i不取,j不取是可以等效成i不取,j没 ...

  8. Luogu 4438 [HNOI/AHOI2018]道路

    $dp$. 这道题最关键的是这句话: 跳出思维局限大胆设状态,设$f_{x, i, j}$表示从$x$到根要经过$i$条公路,$j$条铁路的代价,那么对于一个叶子结点,有$f_{x, i, j} = ...

  9. Luogu P4436 [HNOI/AHOI2018]游戏

    题目 我们要求出\(l_i,r_i\)表示\(i\)最远能够到达的最左边和最右边的格子. 首先有一个比较简单的暴力,就是每次我们选择一个格子,然后从当前格子开始往左右暴力扩展,找到能够到达的最远的格子 ...

随机推荐

  1. MIME协议(三) -- MIME邮件的组织结构

    一封MIME邮件可以由多个不同类型的MIME消息组合而成,一个MIME消息表示邮件中的一个基本MIME资源或若干基本MIME消息的组合体.每个MIME消息的数据格式与RFC822数据格式相似,也包括头 ...

  2. 关于Map的问题

    关于Map的问题主要有: (1)什么是散列表? (2)怎么实现一个散列表? (3)java中HashMap实现方式的演进? (4)HashMap的容量有什么特点? (5)HashMap是怎么进行扩容的 ...

  3. 如何优雅地防止MLE(for linux)

    赛前最后一天模拟赛又有小伙伴MLE了--这里就讲一下如何较为精确地获取程序运行时间和空间. 资源统计当然是操作系统统计的最精确.所以可以这样写(noilinux实测通过,windows下应该不行): ...

  4. 利用Python脚本完成一个Fat-tree型的拓扑

    利用Python脚本完成如下图所示的一个Fat-tree型的拓扑(交换机和主机名需与图中一致,即s1~s6,h1~h8) 参考资料 修改代码如下: from mininet.topo import T ...

  5. 两个int值相乘超过int最大值

    System.out.println(100000000*1000 ); //输出结果是:1215752192 先将100000000*1000 的结果转化为二进制: 10111 01001000 0 ...

  6. 17.树的子结构 Java

    题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 思路 双递归:外层HasSubtree是一个先根遍历递归,内层isDirectSubTree是一个 ...

  7. Leetcode题目104.二叉树的最大深度(DFS+BFS简单)

    题目描述: 给定一个二叉树,找出其最大深度. 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数. 说明: 叶子节点是指没有子节点的节点. 示例: 给定二叉树 [3,9,20,null,null, ...

  8. Mysql忘记密码,支持中文,tab补全

    忘记Mysql密码: (1)暂停服务: /etc/init.d/mysqld stop (2)跳过grant表授权,进入安全模式,并在后台运行: mysqld_safe --skip-grant-ta ...

  9. 基于MybatisUtil工具类,完成CURD操作

    package loaderman; import java.io.IOException; import java.io.Reader; import java.sql.Connection; im ...

  10. css3属性clip

    clip 属性定义了元素的哪一部分是可见的.clip 属性只适用于 position:absolute 的元素. rect(<top>, <right>, <bottom ...