【BZOJ5287】[HNOI2018]毒瘤(动态规划,容斥)
【BZOJ5287】[HNOI2018]毒瘤(动态规划,容斥)
题面
题解
考场上想到的暴力做法是容斥:
因为\(m-n\le 10\),所以最多会多出来\(11\)条非树边。
如果就是一棵树的话,显然答案就是独立集的个数。
非树边\(2^{11}\)枚举,强制非树边的两端同时备选导致不合法,容斥计算答案即可。
这样子的复杂度是\(O(2^{11}n)\),估算出来是\(2s\),然而在\(HNOI\)考场跑要\(20s\)(大雾
考虑如何优化这个东西。
我们\(2^{11}\)枚举出来之后,显然是强制令枚举的非树边的两端都被选入进集合。但是我们并不需要每次重新做一遍\(dp\),显然会出现大量的重复计算内容。
把枚举的点的虚树给构建出来,显然会影响到的部分只有虚树上的点和链。
对于每个虚树上的点,考虑修改后对于其虚树上父亲的影响。
因为\(dp\)状态是\(f[i][0/1]\),所以可以把关键点的状态设为\(x,y\),到虚树上父亲的链的转移全部用\(x,y\)的形式转移,这样子到其父亲时就可以合并一堆\(x,y\)的状态,当确定所有\(x,y\)后就能确定所有虚树上的关键点的\(dp\)值。
这样子单次容斥的复杂度就变成了虚树点数,这个东西很小。
这是一个很类似于动态\(dp\)的思路。
代码有点丑
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 100100
#define MOD 998244353
#define pb push_back
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int fpow(int a,int b)
{
int s=1;
while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
return s;
}
int dsu[MAX];
int getf(int x){return x==dsu[x]?x:dsu[x]=getf(dsu[x]);}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,m,ans,f[MAX][2],zr[MAX][2],g[MAX][2];
int fa[MAX],dfn[MAX],tim,size[MAX],hson[MAX],top[MAX],dep[MAX],low[MAX];
void dfs1(int u,int ff)
{
f[u][0]=f[u][1]=1;fa[u]=ff;dep[u]=dep[ff]+1;size[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
dfs1(v,u);size[u]+=size[v];
if(size[v]>size[hson[u]])hson[u]=v;
if((f[v][0]+f[v][1])%MOD)f[u][0]=1ll*f[u][0]*(f[v][0]+f[v][1])%MOD;else zr[u][0]+=1;
if(f[v][0])f[u][1]=1ll*f[u][1]*f[v][0]%MOD;else zr[u][1]+=1;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;dfn[u]=++tim;
if(hson[u])dfs2(hson[u],tp);
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=fa[u]&&e[i].v!=hson[u])
dfs2(e[i].v,e[i].v);
low[u]=tim;
}
int LCA(int u,int v)
{
while(top[u]^top[v])dep[top[u]]<dep[top[v]]?v=fa[top[v]]:u=fa[top[u]];
return dep[u]<dep[v]?u:v;
}
bool cmp(int a,int b){return dfn[a]<dfn[b];}
int S[MAX],Top,snt;bool spn[MAX];
struct data{int x,y;}nt[50];
data operator*(data a,int b){return (data){1ll*a.x*b%MOD,1ll*a.y*b%MOD};}
data operator+(data a,data b){return (data){(a.x+b.x)%MOD,(a.y+b.y)%MOD};}
vector<int> fr[MAX];
vector<data> F0[MAX],F1[MAX];
int Q[MAX],tot;
int Div(int i,int p,int j)
{
if(j)return zr[i][p]?0:1ll*f[i][p]*fpow(j,MOD-2)%MOD;
else return zr[i][p]==1?f[i][p]:0;
}
void Calc(int x,int y)
{
data f0=(data){1,0},f1=(data){0,1},ff0,ff1;
int p=x;
for(int i=fa[x],j=x;i!=y;p=j=i,i=fa[i])
{
int F0=Div(i,0,(f[j][0]+f[j][1])%MOD),F1=Div(i,1,f[j][0]);
ff0=(f0+f1)*F0;ff1=f0*F1;
f0=ff0;f1=ff1;
}
fr[y].pb(x);F0[y].pb(f0);F1[y].pb(f1);
int a=(f[p][0]+f[p][1])%MOD,b=f[p][0];
if(a)f[y][0]=1ll*f[y][0]*fpow(a,MOD-2)%MOD;else zr[y][0]-=1;
if(b)f[y][1]=1ll*f[y][1]*fpow(b,MOD-2)%MOD;else zr[y][1]-=1;
}
bool Vis[MAX];
int DP()
{
for(int i=Top;i;--i)g[S[i]][0]=zr[S[i]][0]?0:f[S[i]][0],g[S[i]][1]=zr[S[i]][1]?0:f[S[i]][1];
for(int i=Top;i;--i)
if(Vis[S[i]])g[S[i]][0]=0;
for(int i=Top;i;--i)
for(int j=0,l=fr[S[i]].size();j<l;++j)
{
int u=S[i],v=fr[u][j];
data f0=F0[u][j],f1=F1[u][j];
int ff0=(1ll*f0.x*g[v][0]+1ll*f0.y*g[v][1])%MOD;
int ff1=(1ll*f1.x*g[v][0]+1ll*f1.y*g[v][1])%MOD;
g[u][0]=1ll*g[u][0]*(ff0+ff1)%MOD;
g[u][1]=1ll*g[u][1]*ff0%MOD;
}
return (g[1][0]+g[1][1])%MOD;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)dsu[i]=i;
for(int i=1;i<=m;++i)
{
int u=read(),v=read();
if(getf(u)==getf(v))S[++Top]=u,S[++Top]=v,nt[snt++]=(data){u,v};
else Add(u,v),Add(v,u),dsu[getf(u)]=getf(v);
}
dfs1(1,0);dfs2(1,1);S[++Top]=1;
sort(&S[1],&S[Top+1],cmp);
for(int i=Top;i>1;--i)S[++Top]=LCA(S[i],S[i-1]);
sort(&S[1],&S[Top+1],cmp);Top=unique(&S[1],&S[Top+1])-S-1;
for(int i=1;i<=Top;++i)spn[S[i]]=true;
Q[tot=1]=S[1];
for(int i=2;i<=Top;++i)
{
while(!(dfn[Q[tot]]<=dfn[S[i]]&&dfn[S[i]]<=low[Q[tot]]))--tot;
Calc(S[i],Q[tot]);Q[++tot]=S[i];
}
for(int i=0;i<1<<snt;++i)
{
int d=1;
for(int j=0;j<snt;++j)
if(i&(1<<j))
Vis[nt[j].x]=Vis[nt[j].y]=true,d^=1;
int ret=DP();
if(d)ans=(ans+ret)%MOD;
else ans=(ans+MOD-ret)%MOD;
for(int j=0;j<snt;++j)Vis[nt[j].x]=Vis[nt[j].y]=false;
}
printf("%d\n",ans);
return 0;
}
【BZOJ5287】[HNOI2018]毒瘤(动态规划,容斥)的更多相关文章
- BZOJ5287 HNOI2018毒瘤(虚树+树形dp)
显然的做法是暴力枚举非树边所连接两点的选或不选,大力dp.考场上写的是最暴力的O(3n-mn),成功比大众分少10分.容斥或者注意到某些枚举是不必要的就能让底数变成2.但暴力的极限也就到此为止. 每次 ...
- [BZOJ5287][HNOI2018]毒瘤(虚树DP)
暴力枚举非树边取值做DP可得75. 注意到每次枚举出一个容斥状态的时候,都要做大量重复操作. 建立虚树,预处理出虚树上两点间的转移系数.也可动态DP解决. 树上倍增.动态DP.虚树DP似乎是这种问题的 ...
- 【BZOJ4559】[JLoi2016]成绩比较 动态规划+容斥+组合数学
[BZOJ4559][JLoi2016]成绩比较 Description G系共有n位同学,M门必修课.这N位同学的编号为0到N-1的整数,其中B神的编号为0号.这M门必修课编号为0到M-1的整数.一 ...
- [bzoj5287] [HNOI2018]毒瘤
题目描述 从前有一名毒瘤. 毒瘤最近发现了量产毒瘤题的奥秘.考虑如下类型的数据结构题:给出一个数组,要求支持若干种奇奇怪怪的修改操作(比如区间加一个数,或者区间开平方),并支持询问区间和.毒瘤考虑了n ...
- 【LOJ#2542】[PKUWC2018]随机游走(min-max容斥,动态规划)
[LOJ#2542][PKUWC2018]随机游走(min-max容斥,动态规划) 题面 LOJ 题解 很明显,要求的东西可以很容易的进行\(min-max\)容斥,那么转为求集合的\(min\). ...
- 【BZOJ2024】舞会(动态规划,容斥,高精度)
[BZOJ2024]舞会(动态规划,容斥,高精度) 题面 BZOJ 洛谷 题解 这种关系显然要先排序才不会不想影响. 设\(f[i][j]\)表示前\(i\)个女生中,选了\(j\)个女生配对,并且女 ...
- 【BZOJ2839】集合计数(容斥,动态规划)
[BZOJ2839]集合计数(容斥,动态规划) 题面 BZOJ 权限题 Description 一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使 ...
- 【BZOJ3622】已经没有什么好害怕的了(动态规划,容斥)
[BZOJ3622]已经没有什么好害怕的了(动态规划,容斥) 题面 BZOJ 题解 很明显的,这类问题是要从至少变成恰好的过程,直接容斥即可. 首先我们要求的是(糖果>药片)=(药片>糖果 ...
- 【BZOJ3294】放棋子(动态规划,容斥,组合数学)
[BZOJ3294]放棋子(动态规划,容斥,组合数学) 题面 BZOJ 洛谷 题解 如果某一行某一列被某一种颜色给占了,那么在考虑其他行的时候可以直接把这些行和这些列给丢掉. 那么我们就可以写出一个\ ...
随机推荐
- jmeter分布式压测(多台电脑一起压测)
(1)在Windows下运行 操作步骤: 1) 有多台电脑,每台电脑上都有jmeter,而且这几台电脑都互相能ping通. 2) 在我的电脑的jmeter的配置文件bin目录下的jme ...
- ~/.bashrc与/etc/profile的区别
~/.bashrc:该文件包含专用于某个用户的bash shell的bash信息,当该用户登录时以及每次打开新的shell时,该文件被读取. /etc/profile中设定的变量(全局)的可以作用于任 ...
- myeclipse部署报错报空指针异常
hib4.1+spring3+struts2项目 项目运行报错,把WEB-INF/classes目录删除后,想再重新编译并自动部署.再自动部署时总是提示错误: Errors occurred duri ...
- react的项目坑
首先在构造页面时 应该将页面的结构分析好. 在处理数据异步时 将数据结构进行完全的简单结构化. 使用redux时 注意返回的数据是深拷贝还是浅拷贝 否则会产生 数组不为空但是没有值的问题 使用自制数据 ...
- [转帖]Docker save and load镜像保存
Docker save and load镜像保存 https://www.cnblogs.com/zhuochong/p/10064350.html docker save 和 load 以及 imp ...
- [转帖]web安全:通俗易懂,以实例讲述破解网站的原理及如何进行防护!如何让网站变得更安全。
web安全:通俗易懂,以实例讲述破解网站的原理及如何进行防护!如何让网站变得更安全. https://www.cnblogs.com/1996V/p/7458377.html 感谢原作者写的内容 安全 ...
- webservice服务的提供及调用完整代码示例
服务提供方: applicationContext.xml applicationContext-webService.xml 服务调用方:
- v-router几种定义方式
第一种 const router = new VueRouter({ routes: [{ path: '/newSongs', component: require('../views/NewSon ...
- 关于@Param
1,使用@Param注解 当以下面的方式进行写SQL语句时: @Select("select column from table where userid = #{userid} " ...
- 老男孩python学习自修【第二天】字符串用法
实时处理增量日志最佳实践 主要使用f.seek()和f.tell()实现 字符串处理函数: s.find(substr, start, end) 查找子字符串,找不到则返回-1,找到则返回对应的索引 ...