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\)最远能够到达的最左边和最右边的格子. 首先有一个比较简单的暴力,就是每次我们选择一个格子,然后从当前格子开始往左右暴力扩展,找到能够到达的最远的格子 ...
随机推荐
- 2019.6.20 校内测试 NOIP模拟 Day 1 分析+题解
这次是zay神仙给我们出的NOIP模拟题,不得不说好难啊QwQ,又倒数了~ T1 大美江湖 这个题是一个简单的模拟题. ----zay 唯一的坑点就是打怪的时候计算向上取整时,如果用ceil函数一 ...
- Python,初次见面请多指教
特点 1.可读性强: 可读性远比听上去重要的多得多.一个程序会被反复的修改,可读性强意味着让你可以在更短的时间内学习和记忆,直接提高生产率. 2.简洁,简洁,简洁: 研究证明,程序员每天可编写的有效代 ...
- MAC ADDRESS
可以使用手机Wifi或蓝牙的MAC地址作为设备标识,但是并不推荐这么做,原因有以下两点:硬件限制:并不是所有的设备都有Wifi和蓝牙硬件,硬件不存在自然也就得不到这一信息.获取的限制:如果Wifi没有 ...
- apidoc 接口文档系统
代码未动,文档先行.apidoc可以方便地维护接口文档.模拟响应数据.前后端分离.导出PDF文档. 特性说明 可视化编辑:支持表单界面编辑接口,不必手动编辑swagger.json 接口模拟响应:支持 ...
- Python中Bool为False的情况
在python中,以下数值会被认为是False: 为0的数字,包括0,0.0空字符串,包括'', ""表示空值的None空集合,包括(),[],{}其他的值都认为是True. No ...
- C# 7 .NET / CLR / Visual Studio version requirements
C# 7 .NET / CLR / Visual Studio version requirements You do NOT need to target .NET 4.6 and above, ...
- 性能测试 | 服务器CPU使用率高分析实例
前面我们讨论系统调用的时候结论是耗时200ns-15us不等.不过我今天说的我的这个遭遇可能会让你进一步认识系统调用的真正开销.在本节里你会看到一个耗时2.5ms的connect系统调用,注意是毫秒, ...
- SCM是什么?
答: 全称为Software Configuration Management,即为软件配置管理
- leetcode39 组合总和
这道题想到的就是dfs,在累加的和大于或等于target时到达递归树的终点. 代码如下: class Solution { public: vector<vector<int>> ...
- WPF学习笔记 - .Net Framework的分离存储技术
写入: protected override void OnClosed(EventArgs e) { base.OnClosed(e); IsolatedStorageFile f = Isolat ...