题目链接

问题:有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. Nginx常用命令(加入系统服务)

    nginx 服务器重启命令,关闭 nginx -s reload :修改配置后重新加载生效 nginx -s reopen :重新打开日志文件 nginx -t -c /path/to/nginx.c ...

  2. CENTOS 搭建SVN服务器(附自动部署到远程WEB)

    安装subversion服务端 # 安装 yum install -y subversion # 测试是否安装成功 如果显示了版本信息则表示安装成功 svnserve --version;sleep ...

  3. 每天一个Linux命令(60)ip命令

        ip命令是Linux下较新的功能强大的网络配置工具.     (1)用法:     用法:  ip  [OPTIONS]  OBJECT  [COMMAND [ARGUMENTS]]     ...

  4. 吐槽 MySQL数据库jdbc操作,varchar类型占位符问题——单引号造孽

    很长时间不写代码动手能力明显下降很多常见的错误还是经常发生,今天吐血了一次. 简单的坑总是要多跳几次才能甘心.很清晰的记得大学的时候在此坑差点闷死,现在又跳进这个坑了,搞了半天终于知道错在哪里. St ...

  5. Linux Shell编程 test命令

    概述 test 命令是Shell 脚本中用来进行条件判断的. test命令示例 按照文件类型进行判断 测试选项 作 用 -b 文件 判断该文件是否存在,并且是否为块设备文件(是块设备文件为真) -c ...

  6. Windows下MarialDB使用

    命令行控制启动和关闭:mysqld --console     #这样启动ctrl+c即为关闭 启动:双击mysqld.exe即可   #此为后台启动 关闭:mysqladmin -uroot -pr ...

  7. Windos Server 2008 配置定时清理任务

    系统环境:Windos 2008 R2 x64 位 实施方案:自动清理超过两周的备份系统文件. 编写自动清理脚本..bat文件后缀. 打开计划任务

  8. 谷歌浏览器安装jsonview插件方法

    参考https://www.cnblogs.com/whycxb/p/7126116.html,已安装成功.

  9. Python的装饰器实例用法小结

    这篇文章主要介绍了Python装饰器用法,结合实例形式总结分析了Python常用装饰器的概念.功能.使用方法及相关注意事项 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让 ...

  10. google Json

    gradle仓库地址: // https://mvnrepository.com/artifact/com.google.code.gson/gsoncompile group: 'com.googl ...