【BZOJ5287】[HNOI2018]毒瘤(动态规划,容斥)

题面

BZOJ

洛谷

题解

考场上想到的暴力做法是容斥:

因为\(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]毒瘤(动态规划,容斥)的更多相关文章

  1. BZOJ5287 HNOI2018毒瘤(虚树+树形dp)

    显然的做法是暴力枚举非树边所连接两点的选或不选,大力dp.考场上写的是最暴力的O(3n-mn),成功比大众分少10分.容斥或者注意到某些枚举是不必要的就能让底数变成2.但暴力的极限也就到此为止. 每次 ...

  2. [BZOJ5287][HNOI2018]毒瘤(虚树DP)

    暴力枚举非树边取值做DP可得75. 注意到每次枚举出一个容斥状态的时候,都要做大量重复操作. 建立虚树,预处理出虚树上两点间的转移系数.也可动态DP解决. 树上倍增.动态DP.虚树DP似乎是这种问题的 ...

  3. 【BZOJ4559】[JLoi2016]成绩比较 动态规划+容斥+组合数学

    [BZOJ4559][JLoi2016]成绩比较 Description G系共有n位同学,M门必修课.这N位同学的编号为0到N-1的整数,其中B神的编号为0号.这M门必修课编号为0到M-1的整数.一 ...

  4. [bzoj5287] [HNOI2018]毒瘤

    题目描述 从前有一名毒瘤. 毒瘤最近发现了量产毒瘤题的奥秘.考虑如下类型的数据结构题:给出一个数组,要求支持若干种奇奇怪怪的修改操作(比如区间加一个数,或者区间开平方),并支持询问区间和.毒瘤考虑了n ...

  5. 【LOJ#2542】[PKUWC2018]随机游走(min-max容斥,动态规划)

    [LOJ#2542][PKUWC2018]随机游走(min-max容斥,动态规划) 题面 LOJ 题解 很明显,要求的东西可以很容易的进行\(min-max\)容斥,那么转为求集合的\(min\). ...

  6. 【BZOJ2024】舞会(动态规划,容斥,高精度)

    [BZOJ2024]舞会(动态规划,容斥,高精度) 题面 BZOJ 洛谷 题解 这种关系显然要先排序才不会不想影响. 设\(f[i][j]\)表示前\(i\)个女生中,选了\(j\)个女生配对,并且女 ...

  7. 【BZOJ2839】集合计数(容斥,动态规划)

    [BZOJ2839]集合计数(容斥,动态规划) 题面 BZOJ 权限题 Description 一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使 ...

  8. 【BZOJ3622】已经没有什么好害怕的了(动态规划,容斥)

    [BZOJ3622]已经没有什么好害怕的了(动态规划,容斥) 题面 BZOJ 题解 很明显的,这类问题是要从至少变成恰好的过程,直接容斥即可. 首先我们要求的是(糖果>药片)=(药片>糖果 ...

  9. 【BZOJ3294】放棋子(动态规划,容斥,组合数学)

    [BZOJ3294]放棋子(动态规划,容斥,组合数学) 题面 BZOJ 洛谷 题解 如果某一行某一列被某一种颜色给占了,那么在考虑其他行的时候可以直接把这些行和这些列给丢掉. 那么我们就可以写出一个\ ...

随机推荐

  1. mysql常用命令小结

    1.命令行中键入 net start/stop mysql 开启/停止mysql服务2.命令行中键入 mysql -u用户名 -p密码 连接数据库 (以下命令后须加分号';')3.用show语句显示当 ...

  2. 用WSDL4J解析types标签中的内容

    WSDL4J是一种用来解析WSDL文本的常用工具. 但网络上用WSDL4J来解析wsdl文档complexType标签中内容的问题一大堆也没有有效的解决方法.今天在我“遍历”wsdl4j的api文档和 ...

  3. stark组件之delete按钮、filter过滤

    1.构建批量删除按钮 2.filter过滤 3.总结+coding代码 1.构建批量删除按钮 1.admin中每个页面默认都有 2.stark之构建批量删除 3.coding {% extends ' ...

  4. 软件工程(FZU2015) 赛季得分榜,第五回合

    SE_FZU目录:1 2 3 4 5 6 7 8 9 10 11 12 13 积分规则 积分制: 作业为10分制,练习为3分制:alpha30分: 团队项目分=团队得分+个人贡献分 个人贡献分: 个人 ...

  5. asp.net mvc Areas 母版页动态获取数据进行渲染

    经常需要将一些通用的页面元素抽离出来制作成母版页,但是这里的元素一般都是些基本元素,即不需要 进行后台数据交换的基本数据,但是对于一些需要通过后台查询的数据,我们应该怎么传递给前台的母版页呢 这里描述 ...

  6. C# 和 c++的语法不同点

    GC  Garbage Collection 垃圾回收器 自动释放资源 关键字: new 1.创建对象 2.隐藏从父类继承的同名函数 using 1.引用命名空间 2. using(FileStrea ...

  7. 腾讯机试题 AcWing 603 打怪兽

    题目链接:https://www.acwing.com/problem/content/605/ 题目大意: 略 分析: 用dp[i][j]表示用j元钱能在前i只怪兽上所能贿赂到的最大武力值. 有一种 ...

  8. 销售合同金额数据从Excel导入

    一.业务需求 1.新增了销售合同金额的字段,但是老数据没有这个字段:所以销售合同金额从销售合同附件的各品种金额之和. 2.制作好excel字段模板,将此模板发送给销售业务部门来统计并完成excel表格 ...

  9. Java中的super()使用注意

    1)super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)2)this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)3)super: 它引用当前对象的 ...

  10. qtp 自动化测试--点滴 自定义显示工具菜单 trzedit

    tools-customize-toolbars-勾选后关闭 2 trzedit 使用winobject 方法取值 Window("驷惠WIN系列[汽车4S连锁管理软件] 6.") ...