[HNOI/AHOI2018]毒瘤
题目描述
题解
大意:给出一张n个点n+x条边的无向连通图,x很小,求出这个图上最大独立集的方案数。
感觉就是NOIP保卫王国那题的加强版吧。
暴力的话,我们可以考虑在图上随便找一颗生成树,然后把非树边连接的点设置为关键点,然后2^x枚举这些点的选择情况,每次做一遍树形dp就好了。
如果做到这里,那么可以自然而然的想到一个优化:虚树。
就是发现在虚树上的非关键点部分的转移相似,所以我们可以预处理出这些东西来。
我们设f[i][0/1]表示i点的所有不包含关键点的子树的答案。
对于关键点到关键点的转移,我们可以设k[i][0/1][0/1]表示关键点i点到i点的第一个关键点祖先的儿子处i点和那个儿子的选择情况的方案数。
这个直接从i点向上跳,边跳边统计答案就可以算出来了。
注意虚树上的LCA点也要标为关键点。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<vector>
#define N 110009
using namespace std;
typedef long long ll;
const int mod=;
vector<int>vec[N];
inline int rd(){
int x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
struct node{int u,v;}ed[];
ll k[N][][],g[N][],f[N][],ans;
int tot,head[N],dfn[N],p[][N],deep[N],a[N],ttt,n,m,st[N],top;
bool vis[N],dy1[N],dy0[N],tag[N];
struct edge{int n,to;}e[N<<];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
inline void add2(int u,int v){tag[u]=;tag[v]=;vec[u].push_back(v);}////!!!!!!!!!
void dfs(int u,int fa){
dfn[u]=++dfn[];vis[u]=;
for(int i=;(<<i)<=deep[u];++i)p[i][u]=p[i-][p[i-][u]];
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
int v=e[i].to;
if(!vis[v]){deep[v]=deep[u]+;p[][v]=u;dfs(v,u);}
else{a[++a[]]=u;a[++a[]]=v;ed[++ttt]=node{u,v};}
}
}
inline bool cmp(int a,int b){return dfn[a]<dfn[b];}
inline int getlca(int a,int b){
if(deep[a]<deep[b])swap(a,b);
for(int i=;i>=;--i)if(deep[a]-(<<i)>=deep[b])a=p[i][a];
if(a==b)return a;
for(int i=;i>=;--i)if(p[i][a]!=p[i][b])a=p[i][a],b=p[i][b];
return p[][a];
}
void predp(int u,int jin){
f[u][]=f[u][]=;tag[u]=;
for(int i=head[u];i;i=e[i].n){
int v=e[i].to;
if(p[][v]!=u||v==jin||tag[v])continue;
predp(v,jin);
f[u][]=f[u][]*(f[v][]+f[v][])%mod;
f[u][]=f[u][]*f[v][]%mod;
}
}
inline void getnum(int x,int y){
k[x][][]=k[x][][]=;
for(int i=x;p[][i]!=y;i=p[][i]){
predp(p[][i],i);
ll xx=k[x][][],yy=k[x][][];
k[x][][]=f[p[][i]][]*(k[x][][]+k[x][][])%mod;
k[x][][]=f[p[][i]][]*xx%mod;
k[x][][]=f[p[][i]][]*(k[x][][]+k[x][][])%mod;
k[x][][]=f[p[][i]][]*yy%mod;
}
}
void prework(int u){
for(int i=;i<vec[u].size();++i){
int v=vec[u][i];
prework(v);getnum(v,u);
}
f[u][]=f[u][]=;
for(int i=head[u];i;i=e[i].n){
int v=e[i].to;
if(p[][v]!=u||tag[v])continue;
predp(v,);
f[u][]=f[u][]*(f[v][]+f[v][])%mod;
f[u][]=f[u][]*f[v][]%mod;
}
}
void dp(int u){
g[u][]=f[u][];g[u][]=f[u][];
if(dy1[u])g[u][]=;if(dy0[u])g[u][]=;
for(int i=;i<vec[u].size();++i){
int v=vec[u][i];
dp(v);
ll k0=(k[v][][]*g[v][]%mod+k[v][][]*g[v][]%mod)%mod,k1=(k[v][][]*g[v][]%mod+k[v][][]*g[v][]%mod)%mod;
g[u][]=g[u][]*(k0+k1)%mod;g[u][]=g[u][]*k0%mod;
}
}
int main(){
n=rd();m=rd();int u,v;
for(int i=;i<=m;++i){
u=rd();v=rd();
add(u,v);add(v,u);
}
dfs(,);sort(a+,a+a[]+,cmp);
a[]=unique(a+,a+a[]+)-a-;
st[top=]=;
for(int i=;i<=a[];++i){
tag[a[i]]=;
if(a[i]==st[top])continue;
int lca=getlca(st[top],a[i]);
if(lca==st[top]){st[++top]=a[i];continue;}
while(top>){
int x=st[top],y=st[top-];
if(dfn[lca]>=dfn[y]){add2(lca,x);top--;break;}
add2(y,x);top--;
}
if(st[top]!=lca)st[++top]=lca;
if(st[top]!=a[i])st[++top]=a[i];
}
while(top>)add2(st[top-],st[top]),top--;
prework();
for(int i=;i<(<<a[]);++i){
for(int j=;j<=a[];++j)if(i&(<<j-))dy1[a[j]]=,dy0[a[j]]=;else dy0[a[j]]=,dy1[a[j]]=;
bool tagg=;
for(int j=;j<=ttt;++j){
int u=ed[j].u,v=ed[j].v;
if(dy1[u]&&dy1[v]){tagg=;break;}
}
if(tagg)continue;
dp();
(ans+=g[][]+g[][])%=mod;
}
cout<<ans;
return ;
}
[HNOI/AHOI2018]毒瘤的更多相关文章
- #10 //I [HNOI/AHOI2018]毒瘤
题解: 80分做法还是听简单的 对于非树边枚举一下端点状态 然而我也不知道为什么就多t了一个点 具体实现上 最暴力的是3^n次 但是我们可以发现对于i不取,j取 i不取,j不取是可以等效成i不取,j没 ...
- P4426 [HNOI/AHOI2018]毒瘤
挺不错的一个题. 题意即为求一个图的独立集方案数. 如果原图是一棵树,可以直接大力f[x][0/1]来dp. 由于非树边很少,考虑2^11容斥,强制某些点必选,然后再O(n)dp,这样应该过不了. 发 ...
- Luogu P4426 [HNOI/AHOI2018]毒瘤
题目 神仙题. 首先我们可以把题意转化为图的独立集计数.显然这个东西是个NP-Hard的. 然后我们可以注意到\(m\le n+10\),也就是说最多有\(11\)条非树边. 我们现在先考虑一下,树上 ...
- 题解 [HNOI/AHOI2018]毒瘤
题目传送门 题目大意 给出一个 \(n\) 个点 \(m\) 条边的无向图,问有多少个点集满足点集中任意两点均不存在边相连. \(n\le 10^5,m-n\le 10\),答案对 \(9982443 ...
- 洛谷 P4426 - [HNOI/AHOI2018]毒瘤(虚树+dp)
题面传送门 神仙虚树题. 首先考虑最 trival 的情况:\(m=n-1\),也就是一棵树的情况.这个我相信刚学树形 \(dp\) 的都能够秒掉罢(确信).直接设 \(dp_{i,0/1}\) 在表 ...
- [HNOI/AHOI2018]转盘(线段树优化单调)
gugu bz lei了lei了,事独流体毒瘤题 一句话题意:任选一个点开始,每个时刻向前走一步或者站着不动 问实现每一个点都在$T_i$之后被访问到的最短时间 Step 1 该题可证: 最优方案必 ...
- 【LG4437】[HNOI/AHOI2018]排列
[LG4437][HNOI/AHOI2018]排列 题面 洛谷 题解 题面里这个毒瘤的东西我们转化一下: 对于\(\forall k,j\),若\(p_k=a_{p_j}\),则\(k<j\). ...
- 【题解】Luogu P4436 [HNOI/AHOI2018]游戏
原题传送门 \(n^2\)过百万在HNOI/AHOI2018中真的成功了qwqwq 先将没门分格的地方连起来,枚举每一个块,看向左向右最多能走多远,最坏复杂度\(O(n^2)\),但出题人竟然没卡(建 ...
- [Bzoj5285][洛谷P4424][HNOI/AHOI2018]寻宝游戏(bitset)
P4424 [HNOI/AHOI2018]寻宝游戏 某大学每年都会有一次Mystery Hunt的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为新生 ...
随机推荐
- oracle学习笔记(六) JDBC使用
JDBC使用 1. 导包 直接使用IDEA导入依赖包即可 新建一个lib,把jar包放在这里 2. 加载驱动 Class.forName("oracle.jdbc.driver.Oracle ...
- Spring Tool Suite4(sts)复制粘贴卡顿(ctrl+v, ctrl+c)、按住ctrl也很卡
最近在看<Spring in Action, Fifth Edition>,下载了Spring Tool Suite4,在使用的过程中发现了一些问题: 只要在复制粘贴(ctrl+c, ct ...
- 单机Qps上限是多少?
现在这个年代,你要是不懂高并发,你都不好意思说自己是搞互联网的! 一.什么是并发,什么是高并发 并发,两个及以上的行为一起发生,比如你一边吃饭一边看电视 高并发,多个行为(至于是多少,这个没有定数,你 ...
- Python笔记-面向对象编程
1.类和实例 面向-对象的三大特点:数据封装.继承和多态 在Python中,所有数据类型都可以视为对象,当然也可以自定义对象.自定义的对象数据类型就是面向对象中的类(Class)的概念. 假设我们要处 ...
- 局域网git服务器搭建(基于win7 + bonobo git server)
公司内网有一台win7系统的服务器. 准备在上面部署git后台, 用于内网项目版本管理. 搜索了相关资料后, 在根据公司环境, 决定采用win7 + bonobo git server + git的方 ...
- Docker-单宿主机下的网络模式
docker利用namespaces和cgroups实现了应用隔离和资源控制,那么网络层优势如何实现的呢?是直接使用宿主机的网卡设备,还是独立创造出自己的网络设备?以及容器如何与外界通信,下面我们通过 ...
- 【原】无脑操作:EasyUI Tree实现左键只选择叶子节点、右键浮动菜单实现增删改
Easyui中的Tree组件使用频率颇高,经常遇到的需求如下: 1.在树形结构上,只有叶子节点才能被选中,其他节点不能被选中: 2.在叶子节点上右键出现浮动菜单实现新增.删除.修改操作: 3.在非叶子 ...
- block的注意点
1). 在block内部使用外部指针且会造成循环引用情况下,需要用__week修饰外部指针: __weak typeof(self) weakSelf = self; 2). 在block内部如果调用 ...
- 初步了解PE分析
尝试编写代码获取PE文件的信息. 首先使用 CreateFile打开一个PE文件并返回一个用于访问该对象的handle. HANDLE CreateFile( LPCTSTR lpFileName, ...
- Ubuntu 14.04 下使用微软的跨平台轻量级开发神器 Visual Studio Code
因为 Visual Studio Code 不断更新,官方最新 v1.32 的 .deb 包已经不能用于 Ubuntu 14.04 直接安装了. 下载 v1.31 的 deb 包安装即可:https: ...