HDU - 5977 Garden of Eden (树形dp+容斥)
题意:一棵树上有n(n<=50000)个结点,结点有k(k<=10)种颜色,问树上总共有多少条包含所有颜色的路径。
我最初的想法是树形状压dp,设dp[u][S]为以结点u为根的包含颜色集合为S的路径条数,然后FWT(应该叫FMT?)搞一下就行了,复杂度$O(nk2^k)$。奈何内存太大,妥妥地MLE...
看到网上大部分的解法都是点分治,我不禁联想到之前学过的树上任意两点距离的求法(点分治+FFT),心想,这道题用点分治+FWT是不是也能过?于是比着葫芦画瓢写出了这样一段又臭又长的代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N=5e4+,inf=0x3f3f3f3f;
int n,k,a[N],hd[N],ne,vis[N],K,siz[N],tot,rt,mx;
ll dp[<<],ans;
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void FWT(ll* a,int n,int f) {
for(int k=; k<n; k<<=)
for(int i=; i<n; i+=k<<)
for(int j=i; j<i+k; ++j)
a[j+k]+=f==?a[j]:-a[j];
}
void getroot(int u,int fa) {
siz[u]=;
int sz=;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(vis[v]||v==fa)continue;
getroot(v,u);
siz[u]+=siz[v];
sz=max(sz,siz[v]);
}
sz=max(sz,tot-siz[u]);
if(sz<mx)mx=sz,rt=u;
}
void dfs(int u,int fa,int S) {
dp[S]++;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(vis[v]||v==fa)continue;
dfs(v,u,S|a[v]);
}
}
void cal(int u,int ba,int f) {
for(int i=; i<=K; ++i)dp[i]=;
dfs(u,-,a[u]|ba);
FWT(dp,K+,);
for(int i=; i<=K; ++i)dp[i]*=dp[i];
FWT(dp,K+,-);
ans+=dp[K]*f;
}
void solve(int u) {
mx=inf,getroot(u,-),u=rt,cal(u,,),vis[u]=;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(!vis[v])tot=siz[v],cal(v,a[u],-),solve(v);
}
}
ll treepartion() {
ans=,tot=n;
solve();
return ans;
}
int main() {
while(scanf("%d%d",&n,&k)==) {
memset(hd,-,sizeof hd),ne=;
memset(vis,,sizeof vis);
K=(<<k)-;
for(int i=; i<=n; ++i)scanf("%d",&a[i]),a[i]=<<(a[i]-);
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
printf("%lld\n",treepartion());
}
return ;
}
虽然成功地AC了,但是仔细一想:不对啊,这道题FWT的复杂度和子树的大小不是线性相关的啊!所以这样一来,总的复杂度成了$O(nk2^klogn)$,反而增大了。
也就是说,这道题用点分治的作用仅仅是减少了内存的开销,复杂度非但没有减少,反而还多了个logn!
当然除了点分治,这道题还有其他的优化方法,比如sclbgw7大佬利用树链剖分的思想将内存优化到了$O(2^klogn)$,时间复杂度仍为$O(nk2^k)$。
树剖做法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+,inf=0x3f3f3f3f;
int n,k,a[N],hd[N],ne,S,fa[N],son[N],siz[N],tot;
ll dp[][<<],b[<<],A[N],B[N],ans;
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void FWT(ll* a,int n,int f) {
for(int k=; k<n; k<<=)
for(int i=; i<n; i+=k<<)
for(int j=i; j<i+k; ++j) {
ll x=a[j],y=a[j+k];
a[j+k]=f==?y+x:y-x;
}
}
void mul(ll* a,ll* b,ll* c,int n) {
for(int i=; i<n; ++i)A[i]=a[i],B[i]=b[i];
FWT(A,n,),FWT(B,n,);
for(int i=; i<n; ++i)c[i]=A[i]*B[i];
FWT(c,n,-);
}
int newnode() {
int u=tot++;
for(int i=; i<(<<k); ++i)dp[u][i]=;
return u;
}
void dfs1(int u,int f) {
fa[u]=f,son[u]=,siz[u]=;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u])continue;
dfs1(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u,int w) {
if(son[u])dfs2(son[u],w);
for(int i=; i<(<<k); ++i)b[i]=;
b[<<a[u]]=;
if((<<a[u]==S))ans++;
for(int i=; i<(<<k); ++i)if((i|(<<a[u]))==S)ans+=dp[w][i]*;
for(int i=; i<(<<k); ++i)b[i|(<<a[u])]+=dp[w][i];
for(int i=; i<(<<k); ++i)dp[w][i]=b[i];
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u]||v==son[u])continue;
int wv=newnode();
dfs2(v,wv),tot--;
mul(dp[w],dp[wv],b,<<k);
ans+=b[S]*;
for(int i=; i<(<<k); ++i)dp[w][i|(<<a[u])]+=dp[wv][i];
}
}
int main() {
while(scanf("%d%d",&n,&k)==) {
memset(hd,-,sizeof hd),ne=;
S=(<<k)-;
for(int i=; i<=n; ++i)scanf("%d",&a[i]),a[i]--;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
tot=ans=;
dfs1(,-),dfs2(,newnode());
printf("%lld\n",ans);
}
return ;
}
还有Menhera大佬利用基的FMT性质将时间复杂度优化到$O(n2^k)$的做法,看样子有点像是容斥。我个人更倾向于这一种,于是在这个思想的基础上进一步地分析:
题目要求的是包含所有颜色的路径条数。如果包含某个元素集合的路径不太好求,那么不包含某个元素集合的呢?只要把属于这个集合的结点都染成白色,不属于的都染成黑色,则问题就转化成了求一棵树上包含的所有点都是黑色的路径条数,直接dp求一下就行了。于是我们可以利用容斥原理,用所有的路径数减去不包含1个元素集合的路径数,再加上不包含2个元素集合的路径数,再减去不包含3个...就得到了答案。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+,inf=0x3f3f3f3f;
int n,k,a[N],siz[N],hd[N],ne,ppc[<<];
ll ans;
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void dfs(int u,int fa,int f) {
for(int i=hd[u]; ~i; i=e[i].nxt)if(e[i].v!=fa)dfs(e[i].v,u,f);
if(!siz[u])return;
ans+=f;
for(int i=hd[u]; ~i; i=e[i].nxt)if(e[i].v!=fa) {
int v=e[i].v;
ans+=(ll)siz[v]*siz[u]**f;
siz[u]+=siz[v];
}
}
ll solve() {
ans=;
for(int S=(<<k)-; S; --S) {
int f=(k-ppc[S])&?-:;
for(int i=; i<=n; ++i)siz[i]=S>>a[i]&;
dfs(,-,f);
}
return ans;
}
int main() {
ppc[]=;
for(int i=; i<(<<); ++i)ppc[i]=ppc[i>>]+(i&);
while(scanf("%d%d",&n,&k)==) {
memset(hd,-,sizeof hd),ne=;
for(int i=; i<=n; ++i)scanf("%d",&a[i]),a[i]--;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
printf("%lld\n",solve());
}
return ;
}
这种方法的复杂度为什么会比FWT少了k呢?这个k哪里去了呢?我想大概是在FWT的过程中把所有集合的dp值都求出来了,而我们只需要求全集的dp值,因此多做了许多无用功。
(ps:由于题目数据的限制,用map优化的点分治可能会更快一些)
HDU - 5977 Garden of Eden (树形dp+容斥)的更多相关文章
- HDU 5977 Garden of Eden (树形dp+快速沃尔什变换FWT)
CGZ大佬提醒我,我要是再不更博客可就连一月一更的频率也没有了... emmm,正好做了一道有点意思的题,就拿出来充数吧=.= 题意 一棵树,有 $ n (n\leq50000) $ 个节点,每个点都 ...
- HDU 5977 Garden of Eden(点分治求点对路径颜色数为K)
Problem Description When God made the first man, he put him on a beautiful garden, the Garden of Ede ...
- P5405-[CTS2019]氪金手游【树形dp,容斥,数学期望】
前言 话说在\(Loj\)下了个数据发现这题的名字叫\(fgo\) 正题 题目链接:https://www.luogu.com.cn/problem/P5405 题目大意 \(n\)张卡的权值为\(1 ...
- bzoj 4455 [Zjoi2016]小星星 树形dp&容斥
4455: [Zjoi2016]小星星 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 643 Solved: 391[Submit][Status] ...
- [JSOI2019]神经网络(树形DP+容斥+生成函数)
首先可以把题目转化一下:把树拆成若干条链,每条链的颜色为其所在的树的颜色,然后排放所有的链成环,求使得相邻位置颜色不同的排列方案数. 然后本题分为两个部分:将一棵树分为1~n条不相交的链的方案数:将这 ...
- [USACO12FEB] 附近的牛 Nearby Cows - 树形dp,容斥
给你一棵 \(n\) 个点的树,点带权,对于每个节点求出距离它不超过 \(k\) 的所有节点权值和 \(m_i\) 随便定一个根,设\(f[i][j]\)表示只考虑子树,距离为\(j\)的权值和,\( ...
- hdu 5977 Garden of Eden(点分治+状压)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5977 题解:这题一看就知道是状压dp然后看了一下很像是点分治(有点明显)然后就是简单的点分治+状压dp ...
- HDU 5977 Garden of Eden
题解: 路径统计比较容易想到点分治和dp dp的话是f[i][j]表示以i为根,取了i,颜色数状态为j的方案数 但是转移这里如果暴力转移就是$(2^k)^2$了 于是用FWT优化集合或 另外http: ...
- HDU 5977 Garden of Eden (树分治+状态压缩)
题意:给一棵节点数为n,节点种类为k的无根树,问其中有多少种不同的简单路径,可以满足路径上经过所有k种类型的点? 析:对于路径,就是两类,第一种情况,就是跨过根结点,第二种是不跨过根结点,分别讨论就好 ...
随机推荐
- 备注字段长度控制JS
//变更原因备注字符长度控制 function checkChangeLength() { var field = $("#changeReasonDesc").val(); ma ...
- 【转】dmesg 时间转换
dmesg 时间转换 dmesg 输出的格式不易查看,可以通过命令进行转换. 记录如下: 时间查看: date -d "1970-01-01 UTC `echo "$(date + ...
- matlab 三维绘制
1. mesh(Z)语句 mesh(Z)语句可以给出矩阵Z元素的三维消隐图,网络表面由Z坐标点定义,与前面叙述的x-y平面的线格相同,图形由邻近的点连接而成.它可用来显示用其它方式难以输出的包含大量数 ...
- 【HackerRank】QuickSort(稳定快排,空间复杂度O(n))
QuickSort In the previous challenge, you wrote a partition method to split an array into 2 sub-array ...
- iOS面试必备-iOS基础知识
近期为准备找工作面试,在网络上搜集了这些题,以备面试之用. 插一条广告:本人求职,2016级应届毕业生,有开发经验.可独立开发,低薪求职.QQ:895193543 1.简述OC中内存管理机制. 答:内 ...
- 源代码中直接package edu.princeton.cs.algs4还是import edu.princeton.cs.algs4问题
对于前者这个问题,直接在src目录下命名一个包:edu.princeton.cs.algs4 即创建了文件:src->edu->princeton->cs->algs4,然后把 ...
- Linux文件系统及文件储存方式【转】
本文转载自:https://blog.csdn.net/qyp199312/article/details/54927843 前言 Linux文件系统构成 文件式的文件结构 Linux的一个具体文件 ...
- XML 的解析方法
四种XML解析方法: (1)Dom生成和解析XML文档 *解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以使用 DOM 接口来操作这个树结构. * 优点:整个文档树在内存中,便 ...
- Proxy动态代理
Proxy动态代理 package com.test.dynamicproxy; public interface Subject { public void request(); } package ...
- elasticsearch中filter执行原理深度剖析(bitset机制与caching机制)
(1)在倒排索引中查找搜索串,获取document list date来举例 word doc1 doc2 doc3 2017-01-01 * *2017-02-02 * *2017-03-03 ...