洛谷P4426 毒瘤 [HNOI/AHOI2018] 虚树+树上dp
正解:虚树+树上dp
解题报告:
传送门!
首先解释一下题意趴,,,语文70pts选手已经开始看不懂题辣QAQ
大概就是个给一个图,求独立集方案,且保证图是联通的,边的数量最多只比点多10
首先思考如果边的数量=点的数量-1,也就是一棵树的时候怎么搞?
直接树上dp就好,f[i][0/1]:选/不选第i个点方案数,转移就f[i][0]=∏(f[son][0]+f[son][1]),f[i][1]=∏f[son][0]
然后考虑多的边怎么搞呢
从上面那个思路自然而然地可以考虑到,可以暴力枚举非树边的情况,然后一个个计算
这样就有80pts辣!
然后考虑怎么继续优化呢
思考之前我想先问个问题吼
想到这一步有麻油想到辣NOIp2018D2T3保卫王国昂,,,一样是强制几个点选/不选,一样是树上dp,转移方程有一部分区别但差别并不大
所以这里也可以用保卫王国的两个方法做了这题!
第一个!树上倍增+树上dp+虚树
首先可以想到,其实虚树上的状态是不会影响其他节点的dp值的(显然,感性理解即可
所以只用枚举状态在虚树上做树形dp就好
这样就从O(211n)变成辣O(211*11+n)!就过去辣!
具体说下怎么在虚树上做dp
设x是y在虚树上的儿子节点,t是y在实树上的儿子节点
显然存在f[t][0]=k1[x][0]*f[x][0]+k2[x][0]*f[x][1],f[t][1]=k1[x][1]*f[x][0]+k2[x][1]*f[x][1]
所以现在发现只要能把dp系数k求出来,然后一直这么拆下去(就是把x的f什么的也表示出来)就能在每次枚举状态之后O(1)地算出来辣
然后说下怎么求dp系数k呢
对于一条虚树边(u,v),将原树中u到v的路径提出来,从v节点向上跳并暴力转移系数,若遇到分叉则额外转移上分叉处的系数.
由于虚树的性质可以知道分叉的子树内不存在关键点(否则分叉处会作为lca出现在虚树上),因此分叉处的系数可以暴力转移.
然后在6天之后我总算把代码打出来了,,,QAQ
有几个要注意的点,分别港下QAQ
1) 这道题需要记录每条边是否是连接两个两个不可共存的点的,就是要开个bool数组存下来嘛
然后这种是有个小技巧的.就是如果是lsqxx存储的话,显然连接相同两个点的两条边是相邻的
所以直接vis[i]=vis[i^1]=1
但是这里要注意一下,,,如果是这样儿的话ed_cnt要从1开始QAQ
其实应该是所有人都知道的注意点?然而我是第一次用这个所以不知道还调了半天QAQ
2) 这个是比较容易想到的,只是我忽略了所以记录下QAQ
就是在枚举状态的时候,也是要开个bool数组记录某个节点是否强制选/不选嘛
然后这里的话代码不应该是,exi[i]=1
应该是exi[land[i]]=1
另外那个状态是要从2<<1开始,不是2<<0
因为点的编号是从1开始的,从2<<1开始的话就不要做一些杂七杂八的细节辣QAQ
好像麻油了,然后这题细节真的贼多,,,我调了一个下午,,,wsl,,,麻油脑子了QAQ
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
#define rg register
#define gc getchar()
#define mp make_pair
#define t_rl(i) edge_rl[i].to
#define t_fk(i) edge_fk[i].to
#define w_rl(i) edge_rl[i].wei
#define w_fk(i) edge_fk[i].wei
#define rp(i,x,y) for(rg ll i=x;i<=y;++i)
#define my(i,x,y) for(rg ll i=x;i>=y;--i)
#define e_rl(i,x) for(rg ll i=head_rl[x];i;i=edge_rl[i].nxt)
#define e_fk(i,x) for(rg ll i=head_fk[x];i;i=edge_fk[i].nxt)
#define fd_fa(i,x,y) for(rg ll i=x;fa[i][0]!=y;i=fa[i][0]) const ll N=+,inf=1e12,mod=;
ll edge_rl_cnt=,edge_fk_cnt=,head_rl[N],head_fk[N],dep[N],fa[N][],n,m,ld_cnt,land[N],cnt_dfn,dfn[N],stck[N],head_stck,as,fk_num,g[N][],f[N][],k0[N][],k1[N][],poww[N],exi[N];
bool isfked[N<<],infk[N];
struct ed{ll to,nxt;}edge_rl[N<<],edge_fk[N<<];
struct fked{ll fr,to;}fk[N]; il ll read()
{
rg char ch=gc;rg ll x=;rg bool y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il void ad_rl(rg ll x,rg ll y){edge_rl[++edge_rl_cnt]=(ed){y,head_rl[x]};head_rl[x]=edge_rl_cnt;}
il void ad_fk(rg ll x,rg ll y){edge_fk[++edge_fk_cnt]=(ed){y,head_fk[x]};head_fk[x]=edge_fk_cnt;infk[x]=;infk[y]=;}
void dfs_rl(ll nw,ll fat)
{
dfn[nw]=++cnt_dfn;
dep[nw]=dep[fat]+;fa[nw][]=fat;rp(i,,)fa[nw][i]=fa[fa[nw][i-]][i-];
e_rl(i,nw)
{
if(t_rl(i)^fat)
{
if(dfn[t_rl(i)])fk[++fk_num]=(fked){nw,t_rl(i)},isfked[i]=isfked[i^]=;
else dfs_rl(t_rl(i),nw);
}
}
}
il bool cmp(ll gd,ll gs){return dfn[gd]<dfn[gs];}
il ll lca(ll x,ll y)
{
if(dep[x]<dep[y])swap(x,y);
my(i,,)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
my(i,,)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][];
}
il void build_fk()
{
rp(i,,fk_num){if(!infk[fk[i].fr])infk[fk[i].fr]=,land[++ld_cnt]=fk[i].fr;if(!infk[fk[i].to])infk[fk[i].to]=,land[++ld_cnt]=fk[i].to;}
head_stck=edge_fk_cnt=;sort(land+,land++ld_cnt,cmp);if(!infk[])infk[]=,stck[++head_stck]=;
rp(i,,ld_cnt)
{
if(!head_stck)stck[++head_stck]=land[i];
else
{
ll grd=lca(stck[head_stck],land[i]);
if(grd==stck[head_stck]){stck[++head_stck]=land[i];continue;}
while(dep[stck[head_stck-]]>dep[grd])ad_fk(stck[head_stck-],stck[head_stck]),--head_stck;
if(grd!=stck[head_stck-])ad_fk(grd,stck[head_stck]),stck[head_stck]=grd,stck[++head_stck]=land[i];
else ad_fk(grd,stck[head_stck]),stck[head_stck]=land[i];
}
}
while(head_stck>)ad_fk(stck[head_stck-],stck[head_stck]),--head_stck;
}
il void predp(ll x,ll no)
{
f[x][]=f[x][]=,infk[x]=;
e_rl(i,x)
{
if(t_rl(i)==fa[x][] || t_rl(i)==no || infk[t_rl(i)] || isfked[i])continue;
predp(t_rl(i),no);
f[x][]=1ll*f[x][]*(f[t_rl(i)][]+f[t_rl(i)][])%mod;
f[x][]=1ll*f[x][]*f[t_rl(i)][]%mod;
}
}
il void gtk(ll x,ll y)
{
k0[x][]=,k1[x][]=;
fd_fa(i,x,y)
{
predp(fa[i][],i);
ll t0=k0[x][],t1=k1[x][],t3=k0[x][],t4=k1[x][],fat=fa[i][];
k0[x][]=1ll*f[fat][]*(t0+t3)%mod;
k1[x][]=1ll*f[fat][]*(t1+t4)%mod;
k0[x][]=1ll*f[fat][]*t0%mod;
k1[x][]=1ll*f[fat][]*t1%mod;
}
}
il void prewk(ll x)
{
e_fk(i,x)prewk(t_fk(i)),gtk(t_fk(i),x);
f[x][]=f[x][]=;
e_rl(i,x)
{
if(infk[t_rl(i)] || isfked[i] || t_rl(i)==fa[x][])continue;
predp(t_rl(i),);
f[x][]=1ll*f[x][]*(f[t_rl(i)][]+f[t_rl(i)][])%mod;
f[x][]=1ll*f[x][]*f[t_rl(i)][]%mod;
}
}
il void dp(ll x)
{
g[x][]=f[x][];g[x][]=f[x][];
e_fk(i,x)
{
ll to=t_fk(i);dp(to);
ll f0=(1ll*k0[to][]*g[to][]%mod+1ll*k1[to][]*g[to][]%mod)%mod;
ll f1=(1ll*k0[to][]*g[to][]%mod+1ll*k1[to][]*g[to][]%mod)%mod;
g[x][]=1ll*g[x][]*(f0+f1)%mod,g[x][]=1ll*g[x][]*f0%mod;
}
if(exi[x]==)g[x][]=;if(exi[x]==-)g[x][]=;
} int main()
{
freopen("dl.in","r",stdin);freopen("dl.out","w",stdout);
n=read();m=read();
rp(i,,m){ll x=read(),y=read();ad_rl(x,y);ad_rl(y,x);}
dfs_rl(,);build_fk();poww[]=;rp(i,,ld_cnt+)poww[i]=poww[i-]<<;
prewk();
rp(i,,poww[ld_cnt+]-)
{
rp(j,,ld_cnt)if(i&poww[j])exi[land[j]]=;else exi[land[j]]=-;
bool flg=;
rp(j,,fk_num)if(exi[fk[j].fr]== && exi[fk[j].to]==){flg=;break;}
if(flg)continue;
dp();as=(as+(g[][]+g[][])%mod)%mod;
}
printf("%lld\n",as);
return ;
}
/*
依然mk一下取名
g[][]:实际dp的as
f[][]:不考虑虚树balabala的dp的as
k0[][]:dp的系数,不选
k1[][]:同上,选
f0:不选
f1:选
注:k[0/1][][0/1]前一个表虚树上点选不选,后一个表示我选不选
*/
/*
wsl变量太多了,我重新理一下QAQ
edge_rl_cnt:原树的边数
edge_fk_cnt:虚树的边数
head_rl[N]:原树lsqxx
head_fk[M]:虚树lsqxx
dep[N]:原树深度,lca中用
fa[M][20]:倍增,lca中用,之后有要跳到父亲的环节要用
n:树的点对数量
m:边的数量
ld_cnt:不能共存的点的数量
land[M]:不能共存的点对
cnt_dfn:dfn的数量
dfn[N]:dfn序,建虚树排序时用
stck[N]:建虚树时用
head_stck:建虚树时用,栈头指针
as:就是ans,最后求各个状态时乘起来输出
fk_num:不能共存的点对数量
g[N][2],f[N][2],k0[N][2],k1[N][2]:见上,已整理好了
poww[M]:预处理1<<i,方便之后的运算
exi[M]:不能共存的点i是否存在
isfked[N<<1]:原树上的边484连接不能共存的点的
infk[N]:原树上的边484在虚树上?忘了等下写,,,
struct ed{ll to,nxt;}edge_rl[N<<1],edge_fk[M]:原树上的边和虚树上的边
struct fked{ll fr,to;}fk[N]:不能共存的点对
*/这儿是代码QAQ然后因为我把变量名混淆了,,,所以下面打了注释记录各个变量的意义QAQ
第二个!矩阵快速幂预处理!
首先在之前做dp的学习总结中说到优化的时候其实就提到辣
因为dp是一个转移式,也就是个线性递推式,所以我们可以考虑把它变成一个矩阵的形式
然后用一些什么线段树平衡树之类的毒瘤神仙玩意儿维护一下
这个方法是比较正统的,动态dp的做法QwQ
所以显然我还麻油很好的落实只是知道有这么个东西辣
所以代码咕辣QAQ
洛谷P4426 毒瘤 [HNOI/AHOI2018] 虚树+树上dp的更多相关文章
- 洛谷 P2495 [SDOI2011]消耗战(虚树,dp)
题面 洛谷 题解 虚树+dp 关于虚树 了解一下 具体实现 inline void insert(int x) { if (top == 1) {s[++top] = x; return ;} int ...
- 洛谷P4103 [HEOI2014]大工程(虚树 树形dp)
题意 链接 Sol 虚树. 首先建出虚树,然后直接树形dp就行了. 最大最小值直接维护子树内到该节点的最大值,然后合并两棵子树的时候更新一下答案. 任意两点的路径和可以考虑每条边两边的贡献,\(d[x ...
- 洛谷P4425 转盘 [HNOI/AHOI2018] 线段树+单调栈
正解:线段树+单调栈 解题报告: 传送门! 1551又是一道灵巧连题意都麻油看懂的题,,,,所以先解释一下题意好了,,,, 给定一个n元环 可以从0时刻开始从任一位置出发 每次可以选择向前走一步或者在 ...
- bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2286 [题意] 给定一棵树,切断一条树边代价为ci,有m个询问,每次问使得1号点与查询 ...
- bzoj 3611(洛谷 4103) [Heoi2014]大工程——虚树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3611 https://www.luogu.org/problemnew/show/P4103 ...
- 洛谷P2495 [SDOI2011]消耗战(虚树)
题面 传送门 题解 为啥一直莫名其妙\(90\)分啊--重构了一下代码才\(A\)掉-- 先考虑直接\(dp\)怎么做 树形\(dp\)的时候,记一下断开某个节点的最小值,就是从根节点到它的路径上最短 ...
- 洛谷P2495 [SDOI2011]消耗战(虚树dp)
P2495 [SDOI2011]消耗战 题目链接 题解: 虚树\(dp\)入门题吧.虚树的核心思想其实就是每次只保留关键点,因为关键点的dfs序的相对大小顺序和原来的树中结点dfs序的相对大小顺序都是 ...
- 洛谷P2015 二叉苹果树(树状dp)
题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号来 ...
- 洛谷P1122 最大子树和 (树状dp)
题目描述 小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题.一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的问题.于是当日课后,小明 ...
随机推荐
- GPT(保护分区)解决办法
教你在硬盘被GPT保护分区后怎么格式化 GUID 分区表 (GPT) 作为可扩展固件接口 (EFI) 计划的一部分而引入.与 PC 以前通用的旧的主引导记录 (MBR) 分区方案相比,GPT 为磁盘 ...
- SpringMVC Jsp include 错误404 不显示页面
一.问题描述: 1. 新建了taglibs.jsp存放jstl标签库和 jsp建站基本变量ctx 和basPath 如下 (位置WEB-INF\common) <%@ page import= ...
- 纯CSS3冒泡动画按钮实现教程
这款CSS3动画按钮非常的有创意,鼠标在滑过按钮时并不像其他按钮那样仅仅改变按钮的背景颜色,而是出现很酷的冒泡动画.这么惊艳的CSS3动画按钮,这篇文章主要将按钮实现的过程和代码分享给大家,希望能给在 ...
- Nginx对同一IP限速限流
limit_conn_zone是限制同一个IP的连接数,而一旦连接建立以后,客户端会通过这连接发送多次请求,那么limit_req_zone就是对请求的频率和速度进行限制. limit_conn_zo ...
- WebKit最新特性srcset简介(转)
WebKit内核最新新增了对srcset属性的支持(参考:https://www.webkit.org/blog/2910/improved-support-for-high-resolution-d ...
- opencv学习笔记——cv::CommandLineParser函数详解
命令行解析类CommandLineParser 该类的作用主要用于命令行的解析,也就是分解命令行的作用.以前版本没这个类时,如果要运行带参数的.exe,必须在命令行中输入文件路径以及各种参数,并且输入 ...
- 分布式文件系统HDFS,大数据存储实战(一)
本文进行了以下工作: OS中建立了两个文件,文件中保存了几组单词. 把这两个文件导入了hadoop自己的文件系统. 介绍删除已导入hadoop的文件和目录的方法,以便万一发生错误时使用. 使用列表命令 ...
- MapReduce 找出共同好友
这个前提需要注意:好友之间的关系是单向的,我的好友队列里有你,你的里面不一定有我.所以思考方式需要改变. 共同好友: 某两个人的好友队列里都有的人. 第一个mapper 和 reducer 简单说:找 ...
- 【libreOJ模板】并查集(输入挂,取模与find优化)
1.了解了各种输入挂性orz,找到了一个合适的 2.find用while写能快一倍,并且能被数据卡掉 3.取模只能快十几毫秒,但也能被数据卡掉 取模find双优化是1997mm过的 再加一个性价比较高 ...
- 解决sudo: npm: command not found
sudo ln -s /opt/node-v11.4.0/bin/npm /usr/bin/npm sudo ln -s /opt/node-v11.4.0/bin/node /usr/bin/nod ...