BZOJ5287 HNOI2018毒瘤(虚树+树形dp)
显然的做法是暴力枚举非树边所连接两点的选或不选,大力dp。考场上写的是最暴力的O(3n-mn),成功比大众分少10分。容斥或者注意到某些枚举是不必要的就能让底数变成2。但暴力的极限也就到此为止。
每次重新dp做了大量重复的事,考虑从减少重复计算方面优化。先跑一遍没有限制的树形dp。将非树边所连接的点拎出来建一棵虚树。注意到虚树中某点对其父亲的贡献系数(也即由该点到其父亲的链上点的dp值与其关系)是不变的,那么dp出系数,依旧暴力枚举非树边就可以了。一定程度上与noip2018d2t3有相似之处?
码起来非常长(虽然似乎并没有很难写),可能是我姿势不对。注意暴力枚举非树边时不应标记其是否强制被选,而是标记强制被选的次数,防止回溯时出问题。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100050
#define P 998244353
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,p[N],f[N][],id[N][],dfn[N],size[N],fa[N][],deep[N],num[N][][],tmp[][],tot,cnt,t,u,ans=;
bool vis[N],flag[N];
struct data{int to,nxt;
}edge[N<<];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
int ksm(int a,int k)
{
int s=;
for (;k;k>>=,a=1ll*a*a%P) if (k&) s=1ll*s*a%P;
return s;
}
int inv(int a){return ksm(a,P-);}
void dfs(int k)
{
vis[k]=;f[k][]=f[k][]=;dfn[k]=++tot;size[k]=;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k][])
if (vis[edge[i].to])
{
flag[k]=flag[edge[i].to]=;bool tag=;
for (int j=;j<=cnt;j++) if (id[j][]==edge[i].to&&id[j][]==k) {tag=;break;}
if (tag) cnt++,id[cnt][]=k,id[cnt][]=edge[i].to;
}
else
{
fa[edge[i].to][]=k;
deep[edge[i].to]=deep[k]+;
dfs(edge[i].to);
f[k][]=1ll*f[k][]*(f[edge[i].to][]+f[edge[i].to][])%P,
f[k][]=1ll*f[k][]*f[edge[i].to][]%P;
size[k]+=size[edge[i].to];
}
}
int lca(int x,int y)
{
if (deep[x]<deep[y]) swap(x,y);
for (int j=;~j;j--) if (deep[fa[x][j]]>=deep[y]) x=fa[x][j];
if (x==y) return x;
for (int j=;~j;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
return fa[x][];
}
namespace virtual_tree
{
int m,p[N],t,g[N][],point[N],stk[N],top,cho[N];
struct data{int to,nxt;}edge[N<<];
void addedge(int x,int y){t++;flag[x]=flag[y]=;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
bool cmp(const int&a,const int&b){return dfn[a]<dfn[b];}
void build()
{
for (int i=;i<=n;i++) if (flag[i]) point[++m]=i;
sort(point+,point+m+,cmp);
stk[top=]=;
for (int i=(point[]==)+;i<=m;i++)
{
int x=lca(point[i],stk[top]);
if (x==stk[top]) stk[++top]=point[i];
else
{
while (top>&&deep[stk[top-]]>=deep[x])
addedge(stk[top-],stk[top]),top--;
if (stk[top]!=x) addedge(x,stk[top--]),stk[++top]=x;
stk[++top]=point[i];
}
}
while (top>) addedge(stk[top-],stk[top]),top--;
}
void copy(int k)
{
g[k][]=f[k][],g[k][]=f[k][];
for (int i=p[k];i;i=edge[i].nxt)
{
copy(edge[i].to);
g[k][]=1ll*g[k][]*inv((1ll*num[edge[i].to][][]*f[edge[i].to][]+1ll*num[edge[i].to][][]*f[edge[i].to][])%P)%P;
g[k][]=1ll*g[k][]*inv((1ll*num[edge[i].to][][]*f[edge[i].to][]+1ll*num[edge[i].to][][]*f[edge[i].to][])%P)%P;
}
}
void dp(int k)
{
if (cho[k]) g[k][]=;
for (int i=p[k];i;i=edge[i].nxt)
{
dp(edge[i].to);
g[k][]=1ll*g[k][]*((1ll*num[edge[i].to][][]*g[edge[i].to][]+1ll*num[edge[i].to][][]*g[edge[i].to][])%P)%P;
g[k][]=1ll*g[k][]*((1ll*num[edge[i].to][][]*g[edge[i].to][]+1ll*num[edge[i].to][][]*g[edge[i].to][])%P)%P;
}
}
int getans()
{
copy();
dp();
return (g[][]+g[][])%P;
}
void dfs(int k,int s)
{
if (k>cnt) {ans=((ans+s*getans())%P+P)%P;return;}
cho[id[k][]]++,cho[id[k][]]++;dfs(k+,-s);
cho[id[k][]]--,cho[id[k][]]--;dfs(k+,s);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj5287.in","r",stdin);
freopen("bzoj5287.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();
for (int i=;i<=m;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
}
fa[][]=;deep[]=;dfs();
for (int j=;j<;j++)
for (int i=;i<=n;i++)
fa[i][j]=fa[fa[i][j-]][j-];
virtual_tree::build();
for (int i=;i<=n;i++) if (flag[i]) virtual_tree::point[++u]=i;
for (int j=;j<=u;j++)
{
int i=virtual_tree::point[j];
num[i][][]=num[i][][]=;
if (i!=)
{
int x=fa[i][],y=i;
do
{
tmp[][]=(num[i][][]+num[i][][])%P,tmp[][]=(num[i][][]+num[i][][])%P,
tmp[][]=num[i][][],tmp[][]=num[i][][];
num[i][][]=tmp[][],num[i][][]=tmp[][],num[i][][]=tmp[][],num[i][][]=tmp[][];
if (!flag[x])
for (int k=p[x];k;k=edge[k].nxt)
if (edge[k].to!=y&&edge[k].to!=fa[x][])
{
num[i][][]=1ll*num[i][][]*(f[edge[k].to][]+f[edge[k].to][])%P;
num[i][][]=1ll*num[i][][]*(f[edge[k].to][]+f[edge[k].to][])%P;
num[i][][]=1ll*num[i][][]*f[edge[k].to][]%P;
num[i][][]=1ll*num[i][][]*f[edge[k].to][]%P;
}
y=x,x=fa[x][];
}while(!flag[y]);
}
}
virtual_tree::dfs(,);
cout<<ans;
return ;
}
BZOJ5287 HNOI2018毒瘤(虚树+树形dp)的更多相关文章
- 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] ...
- [BZOJ5287][HNOI2018]毒瘤(虚树DP)
暴力枚举非树边取值做DP可得75. 注意到每次枚举出一个容斥状态的时候,都要做大量重复操作. 建立虚树,预处理出虚树上两点间的转移系数.也可动态DP解决. 树上倍增.动态DP.虚树DP似乎是这种问题的 ...
- 【BZOJ-3572】世界树 虚树 + 树形DP
3572: [Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1084 Solved: 611[Submit][Status ...
- 【BZOJ-2286】消耗战 虚树 + 树形DP
2286: [Sdoi2011消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2120 Solved: 752[Submit][Status] ...
- bzoj 2286(虚树+树形dp) 虚树模板
树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 5002 Sol ...
- BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca
BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...
- BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP
题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...
- [WC2018]通道——边分治+虚树+树形DP
题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...
- 2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)
传送门 虚树入门题? 好难啊. 在学习别人的写法之后终于过了. 这道题dp方程很好想. 主要是不好写. 简要说说思路吧. 显然最优值只能够从子树和父亲转移过来. 于是我们先dfs一遍用儿子更新父亲,然 ...
随机推荐
- ansible role 理解
1.roles意为角色,主要用于封装playbook实现复用性.在ansible中,roles通过文件的组织结构来展现.
- C语言中猴子吃桃问题
猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个.第二天早上又将第一天剩下的桃子吃掉一半,有多吃了一个.以后每天早上都吃了前一天剩下的一半零一个.到第 10 天早上想再吃时 ...
- 2015520吴思其 基于《Arm试验箱的国密算法应用》课程设计个人报告
20155200吴思其 基于<Arm试验箱的国密算法应用>课程设计个人报告 课程设计中承担的任务 完成试验箱测试功能4,5,6以及SM3加密实验的实现 测试四 GPIO0按键中断实验 实验 ...
- Spring-data-jpa 学习笔记(二)
通过上一篇笔记的,我们掌握了SpringData的相关概念及简单的用法.但上一篇笔记主要讲的是Dao层接口直接继承Repository接口,然后再自己定义方法.主要阐述了自定义方法时的 ...
- WPF编程,通过Double Animation动态缩放控件的一种方法。
原文:WPF编程,通过Double Animation动态缩放控件的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/art ...
- Luogu P1273 有线电视网
最近写DP写得比较多了 但是POJ上的题目太傻比了而且不想看英文的题面,然后就在Luogu的试炼场里找了一个DP EX专题写了一下(大概3days吧,一天一题差不多) 这是一道比较简单的DP 话说树形 ...
- Educational Codeforces Round 41 (Rated for Div. 2)(A~D)
由于之前打过了这场比赛的E题,而后面两道题太难,所以就手速半个多小时A了前4题. 就当练手速吧,不过今天除了C题数组开小了以外都是1A A Tetris 题意的抽象解释可以在Luogu里看一下(话说现 ...
- RHEL6 最小化系统 编译安装部署zabbix (mysql)
RHEL6 最小化系统 编译安装部署zabbix (mysql)官方说明详细见:https://www.zabbix.com/documentation/4.0/manual/installation ...
- 洛咕3312 [SDOI2014]数表
洛咕3312 [SDOI2014]数表 终于独立写出一道题了...真tm开心(还是先写完题解在写的) 先无视a的限制,设\(f[i]\)表示i的约数之和 不妨设\(n<m\) \(Ans=\su ...
- spring 配置 线程池并使用 springtest 进行测试
在 applicationContext.xml 中配置spring线程池: <!-- 包路径扫描 --> <context:component-scan base-package= ...