题目链接

问题:有n个人,最多选k个,如果选了某个人就必须选他指定的另一个人,问最多能选多少个人。

将每个人所指定的人向他连一条单向边,则每一个点都有唯一的前驱,形成的图是个基环树森林,在同一个强连通分量里的点要么全选,要么全不选。

首先用Tarjan算法将每个强连通分量(基环树上的环)缩成一个点,这样每棵基环树就变成了普通的树了。

定义每颗树上没有入度的点为树根,建立一个虚根与每棵树的根连一条边,将森林转化成树,对根节点求一遍树形背包即可。

树形依赖背包是树形背包的一个特例,即树形背包在根节点上的dp值。

可用siz数组或者bitset优化。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+;
int hd[N],op[N],ne,n,k,dp[N][N],dg[N],siz[N],mx[N],dfn[N],low[N],scc[N],sta[N],tot,nscc,tp;
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++,dg[v]++;}
void Tarjan(int u) {
low[u]=dfn[u]=++tot;
sta[++tp]=u;
int v=op[u];
if(!dfn[v])Tarjan(v),low[u]=min(low[u],low[v]);
else if(!scc[v])low[u]=min(low[u],dfn[v]);
if(low[u]==dfn[u])for(nscc++; !scc[u]; scc[sta[tp--]]=nscc);
}
void getscc() {
memset(scc,,sizeof scc);
memset(dfn,,sizeof dfn);
nscc=tot=,tp=-;
for(int i=; i<=n; ++i)if(!dfn[i])Tarjan(i);
memset(siz,,sizeof siz);
memset(dg,,sizeof dg);
for(int i=; i<=n; ++i)siz[scc[i]]++;
for(int u=; u<=n; ++u) {
int v=op[u];
if(scc[v]!=scc[u])addedge(scc[v],scc[u]);
}
for(int i=; i<=nscc; ++i)if(!dg[i])addedge(,i);
}
void dfs(int u) {
memset(dp[u],,sizeof dp[u]);
dp[u][siz[u]]=;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
dfs(v);
for(int j=siz[u]; j>=; --j)if(dp[u][j])
for(int k=; k<=siz[v]; ++k)if(dp[v][k])
dp[u][j+k]=;
siz[u]+=siz[v];
}
} int main() {
memset(hd,-,sizeof hd),ne=;
scanf("%d%d",&n,&k);
for(int i=; i<=n; ++i)scanf("%d",&op[i]);
getscc();
dfs();
for(int i=k; i>=; --i)if(dp[][i]) {printf("%d\n",i); break;}
return ;
}

bitset优化版:

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+;
int hd[N],op[N],ne,n,k,dg[N],siz[N],dfn[N],low[N],scc[N],sta[N],tot,nscc,tp;
bitset<N> dp[N];
struct E {int v,nxt;} e[N];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++,dg[v]++;}
void Tarjan(int u) {
low[u]=dfn[u]=++tot;
sta[++tp]=u;
int v=op[u];
if(!dfn[v])Tarjan(v),low[u]=min(low[u],low[v]);
else if(!scc[v])low[u]=min(low[u],dfn[v]);
if(low[u]==dfn[u])for(nscc++; !scc[u]; scc[sta[tp--]]=nscc);
}
void getscc() {
memset(scc,,sizeof scc);
memset(dfn,,sizeof dfn);
nscc=tot=,tp=-;
for(int i=; i<=n; ++i)if(!dfn[i])Tarjan(i);
memset(siz,,sizeof siz);
memset(dg,,sizeof dg);
for(int i=; i<=n; ++i)siz[scc[i]]++;
for(int u=; u<=n; ++u) {
int v=op[u];
if(scc[v]!=scc[u])addedge(scc[v],scc[u]);
}
for(int i=; i<=nscc; ++i)if(!dg[i])addedge(,i);
}
void dfs(int u) {
dp[u].reset();
dp[u].set(siz[u]);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
dfs(v);
bitset<N> t=dp[u];
for(int j=; j<N; ++j)if(dp[v].test(j))dp[u]|=t<<j;
}
} int main() {
memset(hd,-,sizeof hd),ne=;
scanf("%d%d",&n,&k);
for(int i=; i<=n; ++i)scanf("%d",&op[i]);
getscc();
dfs();
for(int i=k; i>=; --i)if(dp[].test(i)) {printf("%d\n",i); break;}
return ;
}

Gym - 100502G Outing (强连通缩点+树形依赖背包)的更多相关文章

  1. 【bzoj2427】【软件安装】tarjan缩点+树形依赖背包

    (上不了p站我要死了,侵权度娘背锅) Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上, ...

  2. BZOJ.4182.Shopping(点分治/dsu on tree 树形依赖背包 多重背包 单调队列)

    BZOJ 题目的限制即:给定一棵树,只能任选一个连通块然后做背包,且每个点上的物品至少取一个.求花费为\(m\)时最大价值. 令\(f[i][j]\)表示在点\(i\),已用体积为\(j\)的最大价值 ...

  3. BZOJ.4910.[SDOI2017]苹果树(树形依赖背包 DP 单调队列)

    BZOJ 洛谷 \(shadowice\)已经把他的思路说的很清楚了,可以先看一下会更好理解? 这篇主要是对\(Claris\)题解的简单说明.与\(shadowice\)的做法还是有差异的(比如并没 ...

  4. bzoj4753: [Jsoi2016]最佳团体(分数规划+树形依赖背包)

    菜菜推荐的“水题”虐了我一天T T...(菜菜好强强qwq~ 显然是个分数规划题,二分答案算出p[i]-mid*s[i]之后在树上跑依赖背包,选k个最大值如果>0说明还有更优解. 第一次接触树形 ...

  5. RNQOJ [stupid]愚蠢的矿工(树形依赖背包)

    题意 题目链接 Sol 树形依赖背包板子题 树形依赖背包大概就是说:对于一个点,只有选了它的父亲才能选自身 把dfs序建出来,倒过来考虑 设\(f[i][j]\)表示从第\(i\)个节点往后背包体积为 ...

  6. 【LuoguP1273有线电视网】树形依赖背包

    参考论文http://wenku.baidu.com/view/8ab3daef5ef7ba0d4a733b25.html 参考一篇写的很好的博文http://www.cnblogs.com/GXZC ...

  7. Codeforces Gym100502G:Outing(缩点+有依赖的树形背包)

    http://codeforces.com/gym/100502/attachments 题意:有n个点,容量为tol,接下来n个关系,表示选了第i个点,那么第xi个点就必须被选.问最多可以选多少个点 ...

  8. AcWing 286. 选课 (树形依赖分组背包)打卡

    有依赖的背包 首先依赖的概念,就是一个东西依附与一个东西之上,我们想买附品的话必须要把主品先买下来,这个可以先做下这道题 https://www.cnblogs.com/Lis-/p/11047466 ...

  9. CodeForcesGym 100502G Outing

    Outing Time Limit: 1000ms Memory Limit: 524288KB This problem will be judged on CodeForcesGym. Origi ...

随机推荐

  1. python常用模块——hashlib模块

    Python的hashlib提供了常见的摘要算法,如md5.sha1等 什么是摘要算法了?摘要算法又称哈希算法.散列算法. 它通过一个函数,把任意长度的数据转化魏一个长度固定的数据串(通常用十六进制的 ...

  2. php一些常用功能封装

    //二分查找 function bin_sch($array, $low, $high, $k) { if ($low <= $high) { $mid = intval(($low + $hi ...

  3. Linux基本命令 网络命令

    概述 网络和监控命令类似于这些: hostname, ping, ifconfig, iwconfig, netstat, nslookup, traceroute, finger, telnet, ...

  4. 方法——<37>

    1,返回url参数 /* * 返回参数值 * @method getUrlPara * @papram {string},url中参数名 * @return {string},url中参数值 * */ ...

  5. Laravel 调试利器 —— Laravel Debugbar 扩展包安装及使用教程

    1.简介 Laravel Debugbar 在 Laravel 5 中集成了 PHP Debug Bar ,用于显示调试及错误信息以方便开发.该扩展包包含了一个 ServiceProvider 用于注 ...

  6. DNS 介绍

    DNS 介绍 DNS 为 Domain Name System (域名系统的缩写),它是一种将ip地址转换为对应的主机名或将主机名转换成与之对应的ip地址的一种服务机制.DNS使用TCP和UDP,端口 ...

  7. box-flex兼容写法

    box-flex布局在这几年发生了多次变化,可分为2009版.2011版以及2013版, 区分: display:box(inline-box), box-{*}的格式为2009版 display:b ...

  8. MySQL实验1: 新建一个名为 library 的数据库,包含 book、reader 两张表,根据自己的理解安排表的内容并插入数据。

    数据表(table)简称表,它是数据库最重要的组成部分之一.数据库只是一个框架,表才是实质内容. 实验: 新建一个名为 library的数据库,包含 book.reader两张表,根据自己的理解安排表 ...

  9. Spring中操作Hibernate的几种方式

    1.直接操作模版方式HQL: //通过spring的模版方式来操作Hibernate的HQL语句 return this.getHibernateTemplate().find("from ...

  10. Struts的url-pattern配置问题

    一,servlet容器对url的匹配过程: 当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://loca ...