BZOJ

LOJ

洛谷


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

对于非树边的限制,可以再加一维非树边端点的状态(选没选),能得\(55\)分。

对于一条非树边\((u,v)\),要么是\(u\)选\(v\)不选,要么是\(u\)不选\(v\)选,要么是\(u\)不选\(v\)不选。发现后两种情况可以合并,即可以强制\(u\)选\(v\)不选DP一遍,再强制\(u\)不选\(v\)没有限制DP一遍,加起来就是对的了。对于多条非树边,\(2^{m-n+1}\)枚举非树边两端点的状态即可。能得\(70\sim85\)分(取决于评测机...)。(也可以容斥,强制枚举几条非树边的两端点同时选,其它没有限制)

可以想到对于非树边\((u,v)\),如果\(u,v\)之间的点及之间的子树中都没有非树边端点,那么\(f[v][0/1]\)对\(f[u][0/1]\)的贡献系数是一样的。

也就是说,设\(z\)的父亲是\(y\),\(y\)的父亲是\(x\),且有$$f[y][0]=k_0[z][0]\cdot f[z][0]+k_0[z][1]\cdot f[z][1]\f[y][1]=k_1[z][0]\cdot f[z][0]+k_1[z][1]\cdot f[z][1]$$

设\(x\)只考虑\(y\)子树之外所有子树的贡献时,DP值分别是\(g[x][0/1]\),那么把上面这个\(f[y][0/1]\)代进去有:$$f[x][0]=g[x][0]\cdot(k_0[y][0]+k_1[y][0])\cdot f[y][0]+g[x][0]\cdot(k_0[y][1]+k_1[y][1])\cdot f[y][1]\f[x][1]=g[x][1]\cdot(k_0[z][0]\cdot f[z][0]+k_0[z][1]\cdot f[z][1])$$

同样对于\(fa[x],fa[fa[x]]...\),\(f[z][0/1]\)对它的贡献系数也可以这么类似DP得到。

显然如果非树边\((u,v)\)之间没有非树边端点,那无论\(u,v\)选不选贡献系数是不变的。而所有非树边端点的贡献系数可以\(O(n)\)通过上述DP一遍得到。

具体...对于非树边\((u,v)\),把\((u,v)\)在树上的路径提出来,\(v\)暴力往上跳,同时统计其它子树的贡献。如果在这一过程中遇到了虚树上的点\(w\),就连边\(w\to v\)边的转移系数是\(v\)的系数,然后将\(v\)的系数清零,用\(w\)的系数继续向上更新...如果\(w\)不是虚树上的点,就转移\(v\)的系数。(感觉不太好说...太菜了式子给代错想了好久)

所以就可以把非树边的\(k=2(m-n+1)\)个端点拿出来建虚树,枚举非树边端点的状态后,只在虚树上面做最初的DP,然后乘对应的贡献系数。

设\(k=m-n+1\),复杂度\(O(n+k2^k)\)。

因为虚树上的点是可以确定的,所以可以第一次DFS的时候直接建出虚树并标记虚树上的点。(orz Kelin,替他感到可惜...)

注意边数是\(n+10\)!(不是\(n\)就够→_→)


//12540kb	1096ms
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define mod 998244353
#define Mod(x,v) x>=mod&&(x-=mod)
#define Add(x,v) x+v>=mod?x+v-mod:x+v
#define Mul(x,v) 1ll*(x)*(v)%mod
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define mp std::make_pair
#define pr std::pair<int,int>
typedef long long LL;
const int N=1e5+15; int cnt,Enum,H[N],nxt[N<<1],to[N<<1],f[N][2],g[N][2],sz[N],sta[N];
pr e[23];
bool vis[N],mark[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Coef//ficient
{
int c0,c1;
Coef(int c0=0,int c1=0):c0(c0),c1(c1) {}
inline Coef operator +(const Coef &x)
{
return Coef(Add(c0,x.c0),Add(c1,x.c1));
}
inline Coef operator *(const int x)
{
return Coef(Mul(c0,x),Mul(c1,x));
}
}k[N][2];
struct VirtualTree
{
#define M 105
int Enum,H[N],to[M],nxt[M];
Coef k0[M],k1[M];
inline void AE(int u,int v,Coef c1,Coef c2)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, k0[Enum]=c1, k1[Enum]=c2;
}
}VT; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DFS1(int x,int fa)
{
static bool vis[N];
vis[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa)
if(!vis[v]) DFS1(v,x), sz[x]+=sz[v];
else mark[x]=1, x>v&&(e[cnt++]=mp(x,v),1);
mark[x]|=sz[x]>=2, sz[x]=sz[x]||mark[x];
}
int DFS2(int x)//Build Tree
{
static bool vis[N];
int pos=0;
vis[x]=1, g[x][0]=g[x][1]=1;
for(int i=H[x],v,w; i; i=nxt[i])
if(!vis[v=to[i]])
{
if(!(w=DFS2(v))) g[x][0]=Mul(g[x][0],g[v][0]+g[v][1]), g[x][1]=Mul(g[x][1],g[v][0]);
else if(!mark[x]) k[x][0]=k[v][0]+k[v][1], k[x][1]=k[v][0], pos=w;
else VT.AE(x,w,k[v][0]+k[v][1],k[v][0]);
}
if(!mark[x]) k[x][0]=k[x][0]*g[x][0], k[x][1]=k[x][1]*g[x][1];
else k[x][0]=Coef(1,0), k[x][1]=Coef(0,1), pos=x;
return pos;
}
void DP(int x)
{
f[x][0]=g[x][0], f[x][1]=g[x][1];
if(~sta[x]) f[x][sta[x]^1]=0;
for(int i=VT.H[x],v; i; i=VT.nxt[i])
{
DP(v=VT.to[i]);
f[x][0]=Mul(f[x][0],Mul(f[v][0],VT.k0[i].c0)+Mul(f[v][1],VT.k0[i].c1));
f[x][1]=Mul(f[x][1],Mul(f[v][0],VT.k1[i].c0)+Mul(f[v][1],VT.k1[i].c1));
}
} int main()
{
const int n=read(),m=read();
for(int i=1; i<=m; ++i) AE(read(),read());
DFS1(1,1), mark[1]=1, DFS2(1);
memset(sta,0xff,sizeof sta);
LL ans=0;
for(int s=0,lim=1<<cnt; s<lim; ++s)
{
bool fg=1;
for(int i=0; i<cnt&&fg; ++i)
{
int x=e[i].first,y=e[i].second;
if(s>>i&1) (!sta[x]||sta[y]==1)&&(fg=0), sta[x]=1, sta[y]=0;
else sta[x]==1&&(fg=0), sta[x]=0;
}
if(fg) DP(1), ans+=f[1][0]+f[1][1];
for(int i=0; i<cnt; ++i) sta[e[i].first]=-1, sta[e[i].second]=-1;
}
printf("%lld\n",ans%mod); return 0;
}

BZOJ.5287.[AHOI HNOI2018]毒瘤(虚树 树形DP)的更多相关文章

  1. BZOJ.3611.[HEOI2014]大工程(虚树 树形DP)

    题目链接 要求的和.最大值.最小值好像都可以通过O(n)的树形DP做,总询问点数<=2n. 于是建虚树就可以了.具体DP见DP()函数,维护三个值sum[],mx[],mn[]. sum[]要开 ...

  2. BZOJ 2286 [Sdoi2011]消耗战(虚树+树形DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2286 [题目大意] 出一棵边权树,每次给出一些关键点,求最小边割集, 使得1点与各个关 ...

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

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

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

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

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

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

  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. bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2286 [题意] 给定一棵树,切断一条树边代价为ci,有m个询问,每次问使得1号点与查询 ...

随机推荐

  1. java接口实现

    1.接口中的方法一定是public abstract方法所以类要继承实现接口的时候,一定要去掉abstract修饰符,而且还要标明方法的访问权限一定是public 声明接口不适用public就是友好的 ...

  2. C++ Primer 笔记——类成员指针

    1.当我们初始化一个成员指针或为成员指针赋值时,该指针并没有指向任何数据.成员指针指定了成员而非成员所属的对象,只有当解引用成员指针时,我们才提供对象信息. 2.和普通的函数指针类似,如果成员存在重载 ...

  3. matlab转c++代码实现(主要包含C++ std::vector,std::pair学习,包含数组与常数相乘,数组相加减,将数组拉成一维向量,图片的读入等内容)

    MATLAB部分: xmap = repmat( linspace( -regionW/2, regionW/2, regionW), regionH, 1 );%linspace [x1,x2,N] ...

  4. Exchange Server Notes

    以下信息来自Option响应: HTTP/1.1 200 OK Cache-Control: private Allow: OPTIONS,POST Server: Microsoft-IIS/7.0 ...

  5. Java+selenium之WebDriver的常用方法封装(八)

    总结:WEB UI自动化测试一般采用 POP(面向页面编程),自动化测试框架分三层,有时如果页面如果太多,不好管理,可以面向控件编程,即把控件当作页面,毕竟控件是有限的,所以封装页面的代码量会少很多, ...

  6. 复习reactnative....

    import React, { Component } from 'react'; import { AppRegistry, Text, Image, View, TextInput, Scroll ...

  7. 使用Docker方式运行Mysql(MariaDB)

    两者差不多.我使用的是MariaDB. 下面的docker命令,挂了数据,配置,映射了端口,指定了root密码,服务端编码. 蛮快的! docker run \ --name mariadb \ -v ...

  8. HDFS的一些配置文件修改

    sbin/start-dfs.sh 和 sbin/stop-dfs.sh 在第二行增加如下内容 HDFS_DATANODE_USER=root HDFS_DATANODE_SECURE_USER=hd ...

  9. CallContext

    1.线程本地存储区的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽.2.数据槽不在其他逻辑线程上的调用上下文之间共享. class Program { static Jason_TestEnti ...

  10. maven安装和eclipse集成

    maven作为一个项目构建工具,在开发的过程中很受欢迎,可以帮助管理项目中的bao依赖问题,另外它的很多功能都极大的减少了开发的难度,下面来介绍maven的安装及与eclipse的集成. maven的 ...