Luogu P4426 [HNOI/AHOI2018]毒瘤
题目
神仙题。
首先我们可以把题意转化为图的独立集计数。显然这个东西是个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]毒瘤的更多相关文章
- P4426 [HNOI/AHOI2018]毒瘤
挺不错的一个题. 题意即为求一个图的独立集方案数. 如果原图是一棵树,可以直接大力f[x][0/1]来dp. 由于非树边很少,考虑2^11容斥,强制某些点必选,然后再O(n)dp,这样应该过不了. 发 ...
- 洛谷 P4426 - [HNOI/AHOI2018]毒瘤(虚树+dp)
题面传送门 神仙虚树题. 首先考虑最 trival 的情况:\(m=n-1\),也就是一棵树的情况.这个我相信刚学树形 \(dp\) 的都能够秒掉罢(确信).直接设 \(dp_{i,0/1}\) 在表 ...
- 【题解】Luogu P4436 [HNOI/AHOI2018]游戏
原题传送门 \(n^2\)过百万在HNOI/AHOI2018中真的成功了qwqwq 先将没门分格的地方连起来,枚举每一个块,看向左向右最多能走多远,最坏复杂度\(O(n^2)\),但出题人竟然没卡(建 ...
- luogu P4437 [HNOI/AHOI2018]排列
luogu 问题本质是把\(a_i\)作为\(i\)的父亲,然后如果有环就不合法,否则每次要取数,要满足取之前他的父亲都被取过(父亲为0可以直接取),求最大价值 贪心想法显然是要把权值大的尽量放在后面 ...
- [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 ...
- 【题解】Luogu P4438 [HNOI/AHOI2018]道路
原题传送门 实际就是一道简单的树形dp 设f[u][i][j]表示从根结点到结点u经过i条未翻修公路,j条未翻修铁路的贡献最小值 边界条件:f[leaf][i][j]=(A+i)(B+j)C (题目上 ...
- #10 //I [HNOI/AHOI2018]毒瘤
题解: 80分做法还是听简单的 对于非树边枚举一下端点状态 然而我也不知道为什么就多t了一个点 具体实现上 最暴力的是3^n次 但是我们可以发现对于i不取,j取 i不取,j不取是可以等效成i不取,j没 ...
- Luogu 4438 [HNOI/AHOI2018]道路
$dp$. 这道题最关键的是这句话: 跳出思维局限大胆设状态,设$f_{x, i, j}$表示从$x$到根要经过$i$条公路,$j$条铁路的代价,那么对于一个叶子结点,有$f_{x, i, j} = ...
- Luogu P4436 [HNOI/AHOI2018]游戏
题目 我们要求出\(l_i,r_i\)表示\(i\)最远能够到达的最左边和最右边的格子. 首先有一个比较简单的暴力,就是每次我们选择一个格子,然后从当前格子开始往左右暴力扩展,找到能够到达的最远的格子 ...
随机推荐
- 为orangepi zero编译安装nginx记录
使用的系统是armbian 1.下载nginx源代码 wget http://nginx.org/download/nginx-1.17.0.tar.gz 2.解压nginx源代码 tar xvzf ...
- 条件随机场和CRF++使用
参考资料 条件随机场和CRF++使用: http://midday.me/article/94d6bd4973264e1a801f8445904a810d 基于CRF++的中文分词 http://ww ...
- poj1737
Connected Graph POJ - 1737 An undirected graph is a set V of vertices and a set of E∈{V*V} edges.An ...
- mov 与 lea 区别
转自:https://blog.csdn.net/fengyuanye/article/details/85715565 https://my.oschina.net/guonaihong/blog/ ...
- mysql: error while loading shared libraries: libnuma.so
安装mysql后,执行初始化配置脚本,创建系统自带的数据库和表时报异常: [root@VM_0_12_centos mysql]# scripts/mysql_install_db --basedir ...
- Guava中Lists.partition(List, size) 方法懒划分/懒分区
目录 Guava中Lists.partition(List, size) 方法懒划分/懒分区 背景 分析 总结 Guava中Lists.partition(List, size) 方法懒划分/懒分区 ...
- 处理flutter http请求添加application/json报错Cannot set the body fields of a Request with content-type “application/json”
在flutter中在http请求发送时设置"content-type": "application/json"会出现报错Cannot set the body ...
- Qt 互斥量 QMutex
QMutex类提供了一种保护一个变量和一段代码的方法. mutex.lock() //锁住互斥量(mutex).如果互斥量是解锁的,那么当前线程就立即占用并锁定它.否则,当前线程就会被阻塞,知道掌握这 ...
- Oracle 报错 ORA-03290的处置
MySql 的tuancate命令是直接truncate tableName,但在Oracle需要写成truncate table tableName,改正就好了. --END-- 2019.10.1 ...
- C#.net winform skin 皮肤大全
C#.net winform skin 皮肤大全 1. 东日IrisSkin IrisSkin 共有两个版本,一个是IrisSkin.dll 用于.Net Framework1.0/1.1 和Iris ...