显然的做法是暴力枚举非树边所连接两点的选或不选,大力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)的更多相关文章

  1. 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] ...

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

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

  3. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

  4. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

  5. bzoj 2286(虚树+树形dp) 虚树模板

    树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5002  Sol ...

  6. BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca

    BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...

  7. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

  8. [WC2018]通道——边分治+虚树+树形DP

    题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...

  9. 2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)

    传送门 虚树入门题? 好难啊. 在学习别人的写法之后终于过了. 这道题dp方程很好想. 主要是不好写. 简要说说思路吧. 显然最优值只能够从子树和父亲转移过来. 于是我们先dfs一遍用儿子更新父亲,然 ...

随机推荐

  1. Android 一个相对完整的自动升级功能实现代码

    由于项目的需要最近做了一个关于Android自动升级的功能,下面将贴出Android手机客户端的完整代码.这段代码参考别的代码居多,由于不满足需求,所以自己仅仅改了一些需要变动的内容,其他功能都是按照 ...

  2. struts2_Interceptor

    题目要求:要求当未登录访问某些Action时,自动跳转到登录界面. 1. 2. 3. 4. 5.默认拦截器堆栈为defautStack,但一旦用户添加了拦截器,默认拦截器失效 6. 7. struts ...

  3. 【干货】YUM安装PHP 7版本后,增加phalcon框架的报错解决

    目录 1.yum安装php 7.x版本,此处部署7.3版本 2.安装phalcon框架 2.1.PHP版本依赖关系 2.2.编译phalcon扩展模块 2.3.增加扩展文件 3.部署phalcon遇到 ...

  4. JavaScript 为什么不要使用 eval

    本文内容 eval 隐藏的 eval 安全问题 结论 参考资料   eval eval 函数是一个高等级的函数,它与任何对象都无关.其参数,如果是一个字符串表达式,那么该函数计算表达式的值:如果是一个 ...

  5. sonarqube扫描安卓代码

    代码才用https://github.com/liwanlei/bilibili-android-client 配置: build.gralde配置 buildscript { repositorie ...

  6. 通过C#的HttpClient模拟form表单请求

    post提交表单一般无非是一般text文本和文件类型,如下 <input type="file"/> <input type="text"/& ...

  7. js 二进制操作

    //二进制保存var content = "file content!"; var data = new Blob([content],{type:"text/plain ...

  8. linux中使sqlplus能够上下翻页

    安装包链接:https://pan.baidu.com/s/1WsQTeEQClM88aEqIvNi2ag 提取码:s241  rlwrap-0.37-1.el6.x86_64.rpm 和 rlwra ...

  9. Hadoop 部署文档

    Hadoop 部署文档 1 先决条件 2 下载二进制文件 3 修改配置文件 3.1 core-site.xml 3.2 hdfs-site.xml 3.3 mapred-site.xml 3.4 ya ...

  10. [T-ARA][놀아볼래?][要玩吗]

    歌词来源:http://music.163.com/#/song?id=22704479 作曲 : 赵英秀/김태현 [作曲 : 赵英秀/k/gim-Tae-hyeon] 作词 : 安英民 [作词 : ...