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一遍用儿子更新父亲,然 ...
随机推荐
- Jmeter之目录结构
首先得了解一下这些东西,以后才能快速的找到某些配置文件进行修改(举个例子,改配置只是其中之一) 一.bin目录examples: 目录中有CSV样例 jmeter.bat windows的启动文件 ...
- poj 1932 XYZZY(spfa最长路+判断正环+floyd求传递闭包)
XYZZY Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 4154 Accepted: 1185 Description ...
- day61
Vue 八.重要指令 v-bind <!-- 值a --> <div v-bind:class='"a"'></div> <!-- 变量a ...
- DB2创建视图view
create view v_table1(col1,col2,col3...)--视图名(字段一,字段二,字段三...) as --后跟查询语句 select col1,col2,col3... fr ...
- 2017战略No.2:开始电子化记账
一.懒散的4年 大学毕业后,就没有怎么记账了. 自己花的钱,心里有个大概,但是不能算得很具体. 比如说,2016年,又没有攒几个钱,心里多少有点压抑. 大脑去算账,只能算房租吃饭等金额较大的开销,更多 ...
- python 回溯法 子集树模板 系列 —— 19、野人与传教士问题
问题 在河的左岸有N个传教士.N个野人和一条船,传教士们想用这条船把所有人都运过河去,但有以下条件限制: (1)修道士和野人都会划船,但船每次最多只能运M个人: (2)在任何岸边以及船上,野人数目都不 ...
- 【python笔记】使用matplotlib,pylab进行python绘图
一提到python绘图,matplotlib是不得不提的python最著名的绘图库,它里面包含了类似matlab的一整套绘图的API.因此,作为想要学习python绘图的童鞋们就得在自己的python ...
- 解决 引入本地jar包后 maven无法编译的问题及部署war包缺失本地jar包的问题
参考:https://blog.csdn.net/wang864676212/article/details/82626922 pom.xml 引入 <plugin> <plugin ...
- 工具神器推荐 Vox 和 search everything
工具神器推荐 Vox 和 search everything vox官网: http://www.wox.one/
- Unity3D Shader 学习笔记(一):初识Shader
第一节:图形处理器简史 GPU发展简史 GPU英文全称Graphic Procssing Unit. T&L变换和光照流水线 可编程GPU GPU的优点和缺点 第二节:Unity Shader ...