显然的做法是暴力枚举非树边所连接两点的选或不选,大力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. TCP和UDP套接字编程 (java实现)

    在了解网络编程之前,我们先了解一下什么叫套接字 套接字即指同一台主机内应用层和运输层之间的接口 由于这个套接字是建立在网络上建立网络应用的可编程接口 因此也将套接字称为应用程序和网络之间的应用程序编程 ...

  2. W25Q128BV W25Q128FV W25Q128JV 什么区别?

    W25Q128BV  W25Q128FV  W25Q128JV  什么区别?    官网没有找到  BV 的相关信息, 倒是有 FV,FW,JV,JW 信息..2019年1月19日19:09:55 F ...

  3. win10触摸板手势

    尴尬的发现,今天才开始使用win10的手势,之前都是单指操作/笑哭 参考:http://www.sohu.com/a/63678223_230077 https://support.microsoft ...

  4. POJ 1988&&2236

    并查集,如果只是朴素的路径压缩,那么也就是一句话的事情. 但是,一般都没有这种仁慈的裸题(假的,多了去了) 1988:带权并查集,贼鸡儿像Luogu的那道杨威利的并查集(好像是叫银河英雄传说) 开两个 ...

  5. mfc 进程的优先级

    知识点:  进程优先级  获取当前进程句柄  优先级设置  优先级变动  优先级获取 一.进程优先级(优先级等级) 简单的说就是进程(线程)的优先级越高,那么就可以分占相对多的CPU时间片. ...

  6. python 井字棋(Tic Tac Toe)

    说明 用python实现了井字棋,整个框架是本人自己构思的,自认为比较满意.另外,90%+的代码也是本人逐字逐句敲的. minimax算法还没完全理解,所以参考了这里的代码,并作了修改. 特点 可以选 ...

  7. JQ_五星级评分特效

    代码如下:<!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" conte ...

  8. vsftp在防火墙开启需要开放的端口

    1.开放tcp端口 firewall-cmd --zone=public --add-port=20/tcp --permanent firewall-cmd --zone=public --add- ...

  9. 手机Gmail上用Exchange协议配置收发QQ邮箱

    1.开启Exchange服务  2.生成授权码(登录密码)  3."服务器"填入ex.qq.com

  10. 深入浅出etcd系列Part 1 – etcd架构和代码框架

    1.绪论 etcd作为华为云PaaS的核心部件,实现了PaaS大多数组件的数据持久化.集群选举.状态同步等功能.如此重要的一个部件,我们只有深入地理解其架构设计和内部工作机制,才能更好地学习华为云Ku ...