Description

Solution

\(dfs\) 出一棵生成树之后,多出来的边就都是反祖边了

把反祖边两个端点都拿出来,就会得到最多 \(k=2*(m-n+1)\) 个关键点

除了关键点以外的点转移都是一样的,我们可以预处理出来

关键点数量不多,我们 \(2^k\) 枚举状态,然后像树形 \(DP\) 一样转移就行了

转移需要构一棵虚树,对于虚树上的一条边,对应在原树上的一条链转移也是一样的

如果知道了虚树上 \(x\) 的 \(DP\) 值,\(f[x][0],f[x][1]\),那么就可以推出虚树上的父亲的值 \(f[fa[x]][0],f[fa[x]][1]\)

大致可以表示成这样的形式:\(f[fa[x]][0]=k0*f[x][0]+k1*f[x][1]\),\(f[fa[x]][1]\) 同理

对于转移系数和虚树上某些节点的 \(DP\) 初值都可以 \(O(n*k)\) 的预处理出来

对于一条边 \((x,y)\),只有三种状态:存在 \(x\),存在 \(y\),都不存在,所以状态数实际上只有 \(3^{\frac{k}{2}}\)

复杂度是 \(O(n*k+3^{\frac{k}{2}}*k)\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,mod=998244353;
int n,m,head[N],nxt[N*4],to[N*4],num=1,st[N],top=0,sq[N],fa[N][20];
int ST[N*2],TOP=0,dfn[N],DFN=0,tp=0,dep[N],q[N],r=0,Head[N],id[N];
inline bool comp(int i,int j){return dfn[i]<dfn[j];}
inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
inline void Link(int x,int y){nxt[++num]=Head[x];to[num]=y;Head[x]=num;}
inline int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int deep=dep[x]-dep[y];
for(int i=19;i>=0;i--)if(deep>>i&1)x=fa[x][i];
if(x==y)return x;
for(int i=19;i>=0;i--)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
bool vis[N],et[N*4];int imp[N],lim,ans=0,f[N][2],lis[N];
struct data{
int k0,k1;
data(){}
data(int _k0,int _k1){k0=_k0;k1=_k1;}
inline data operator +(data &t){return data((k0+t.k0)%mod,(k1+t.k1)%mod);}
inline data operator *(int t){
return data(1ll*k0*t%mod,1ll*k1*t%mod);}
inline int F(int x,int y){return (1ll*x*k0+1ll*y*k1)%mod;}
}k[N][2];
inline void build(int x,int last){
vis[x]=1;dfn[x]=++DFN;
for(int i=head[x];i;i=nxt[i]){
if(i==last)continue;
int u=to[i];
if(!vis[u])fa[u][0]=x,dep[u]=dep[x]+1,build(u,i^1);
else if(dep[u]<dep[x])st[++top]=x,sq[top]=u,et[i]=et[i^1]=1;
}
}
int dp[N][2];bool d[N];
inline void calc(int x,int la){
dp[x][0]=dp[x][1]=1;
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(u==la || u==fa[x][0] || d[u])continue;
calc(u,la);
dp[x][0]=1ll*dp[x][0]*(dp[u][0]+dp[u][1])%mod;
dp[x][1]=1ll*dp[x][1]*dp[u][0]%mod;
}
}
inline void getit(int S,int T){
int x=S;
k[x][0]=data(1,0);k[x][1]=data(0,1);
while(fa[x][0]!=T){
calc(fa[x][0],x);
data t=k[S][0];d[fa[x][0]]=1;
k[S][0]=(k[S][0]+k[S][1])*dp[fa[x][0]][0];
k[S][1]=t*dp[fa[x][0]][1];
x=fa[x][0];
}
}
inline void DFS(int x){
for(int i=Head[x];i;i=nxt[i]){
DFS(to[i]);
getit(to[i],x);
}
dp[x][0]=dp[x][1]=1;
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(u==fa[x][0] || d[u] || et[i])continue;
calc(u,x);
dp[x][0]=1ll*dp[x][0]*(dp[u][0]+dp[u][1])%mod;
dp[x][1]=1ll*dp[x][1]*dp[u][0]%mod;
}
}
inline void dfs(int x){
f[x][0]=dp[x][0];f[x][1]=dp[x][1];
for(int i=Head[x];i;i=nxt[i]){
int u=to[i],f0,f1;
dfs(u);
f0=k[u][0].F(f[u][0],f[u][1]);
f1=k[u][1].F(f[u][0],f[u][1]);
f[x][0]=1ll*f[x][0]*(f0+f1)%mod;
f[x][1]=1ll*f[x][1]*f0%mod;
}
if(imp[x]!=-1)f[x][imp[x]^1]=0;
}
int main(){
freopen("duliu.in","r",stdin);
freopen("duliu.out","w",stdout);
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
link(x,y);link(y,x);
} dep[1]=1;build(1,-1);
for(int j=1;j<20;j++)
for(int i=1;i<=n;i++)fa[i][j]=fa[fa[i][j-1]][j-1];
for(int i=1;i<=top;i++)ST[++TOP]=st[i],ST[++TOP]=sq[i];
sort(ST+1,ST+TOP+1,comp);
tp=unique(ST+1,ST+TOP+1)-ST-1;
int cnt=0;
for(int i=1;i<=tp;i++)lis[++cnt]=ST[i];
lis[++cnt]=1;
sort(lis+1,lis+cnt+1,comp);
cnt=unique(lis+1,lis+cnt+1)-lis-1;
for(int i=1;i<=tp;i++)id[ST[i]]=i-1; q[++r]=lis[1];
for(int i=2;i<=cnt;i++){
x=lis[i];
int lca=LCA(x,lis[i-1]);d[lca]=1;
while(r && dfn[q[r]]>dfn[lca]){
if(dfn[q[r-1]]>dfn[lca])Link(q[r-1],q[r]);
else {
Link(lca,q[r]);r--;
if(q[r]!=lca)q[++r]=lca;break;
}
r--;
}
q[++r]=lis[i];
}
while(r>1)Link(q[r-1],q[r]),r--; for(int i=1;i<=cnt;i++)d[lis[i]]=1;
DFS(1);lim=(1<<tp)-1;
memset(imp,-1,sizeof(imp));
for(int i=0;i<=lim;i++){
bool flag=1;
for(int j=1;j<=top;j++)
if((i>>id[st[j]]&1) && (i>>id[sq[j]]&1)){flag=0;break;}
if(!flag)continue;
for(int j=1;j<=tp;j++)imp[ST[j]]=i>>(j-1)&1;
dfs(1);
ans=((ans+f[1][0])%mod+f[1][1])%mod;
}
cout<<ans<<endl;
return 0;
}

bzoj 5287: [Hnoi2018]毒瘤的更多相关文章

  1. BZOJ 5287: [Hnoi2018]毒瘤 动态dp(LCT+矩阵乘法)

    自己 yy 了一个动态 dp 做法,应该是全网唯一用 LCT 写的. code: #include <bits/stdc++.h> #define ll long long #define ...

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

    [BZOJ5287][HNOI2018]毒瘤(动态规划,容斥) 题面 BZOJ 洛谷 题解 考场上想到的暴力做法是容斥: 因为\(m-n\le 10\),所以最多会多出来\(11\)条非树边. 如果就 ...

  3. BZOJ.5287.[AHOI HNOI2018]毒瘤(虚树 树形DP)

    BZOJ LOJ 洛谷 设\(f[i][0/1]\)表示到第\(i\)个点,不选/选这个点的方案数.对于一棵树,有:\[f[x][0]=\prod_{v\in son[x]}(f[v][0]+f[v] ...

  4. [bzoj5287] [HNOI2018]毒瘤

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

  5. [HNOI2018]毒瘤

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

  6. bzoj 5286: [Hnoi2018]转盘

    Description Solution 首先注意到一个点不会走两次,只会有停下来等待的情况,把序列倍长 那么如果枚举一个起点\(i\),答案就是 \(min(max(T[j]+n-(j-i)-1)) ...

  7. bzoj 5285: [Hnoi2018]寻宝游戏

    Description Solution 把输入的 \(n\) 个二进制数看作一个大小为 \(n*m\) 的矩阵 把每一列压成一个二进制数,其中最高位是最下面的元素 然后就有了 \(m\) 个二进制数 ...

  8. bzoj 5289: [Hnoi2018]排列

    Description Solution 首先注意到实际上约束关系构成了一棵树 考虑这个排列 \(p\),编号为 \(a[i]\) 的出现了,\(i\) 才可以出现 那么如果连边 \((a[i],i) ...

  9. bzoj 5288: [Hnoi2018]游戏

    Description Solution 乱搞能A的题,毁我青春 记忆化一下扩展过程 只要不是从 \(1\) 枚举到 \(n\) 去扩展都可以 \(AC\) 于是 \(random\_shuffle\ ...

随机推荐

  1. Python基于共现提取《釜山行》人物关系

    Python基于共现提取<釜山行>人物关系 一.课程介绍 1. 内容简介 <釜山行>是一部丧尸灾难片,其人物少.关系简单,非常适合我们学习文本处理.这个项目将介绍共现在关系中的 ...

  2. 201621123031 《Java程序设计》第5周学习总结

    作业05-继承.多态.抽象类与接口 1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 关键字:接口.继承.多态 1.2 尝试使用思维导图将这些关键词组织起来.注:思维导图一般不需 ...

  3. python array 使用创建10万浮点数

    from array import array from random floats = array('d',random((for i in range(10**7)) fp = open('flo ...

  4. 使用JDBC中的出现的乱码和查询无结果问题

    使用JDBC中的问题 连接的后出现查询结果是乱码. 1.可能是代码的编码与数据库的编码不同 ​ 有可以将二者都设置为UTF-8 2.如果比较懒得话可以只设代码为UTF-8 mysql 连接url中us ...

  5. Windows中添加自己的程序到开机启动中(添加服务,添加注册表)

    在系统启动的时候启动自己想要启动的程序: 方法一:利用开机启动文件夹 将exe文件或exe文件的快捷方式复制到(启动)文件夹下 以win7为例:开始→所有程序→启动→鼠标右键打开 方法二:添加系统服务 ...

  6. EasyUI datagrid 使用小结

    用了 EasyUI 框架一段时间了,这个前端框架用起来还是挺方便的,也有很多现成的控件,看看官方文档应该还是能比较快用起来的. 在这里记录一下一些常用的控件的方法,遇到过的bug或者当初耗了一点时间来 ...

  7. crontab的mysqldump备份任务未能完全正确执行的故障处理

    crontab是每个运维一线人员必须掌握的技术,熟练运用crontab可以自动帮助我们执行重复性的工作,提高运维的工作效率.它就像一个闹钟,在特定的时间,准时响应并执行相应的任务.如果你的工作经常与L ...

  8. RxJava系列3(转换操作符)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  9. Dapper中条件为In的写法

    今天用Dapper更新是用到了IN写法,园子里找了篇文章这样写到 传统sql in (1,2,3) 用dapper就这样写 conn.Query<Users>("SELECT * ...

  10. mysql 存储过程 实现数据同步

    数据库 表 发生变化,需要把2.0的表数据 同步到3.0库中去: -- 同步数据存储过程执行 -- 更新留言旧表数据到新表数据中 /*DEFINER:Vector*/ drop procedure i ...