题目链接

问题:有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. 2015/7/29 (高开,V形反转,各种指标背离——可惜没买进,填补空缺图形的心理分析)

    1.李大--謝先生℡:早盘决策:如今日再次出现大幅低开  或者盘中大幅下跌可逢低 3成仓位左右分散资金做短线抄底,切记是超短 绝不追高,设置5个点止损.市场有很多名家在谈论3373点即前低点,本人告诉 ...

  2. JavaScript:学习笔记(6)——New运算符

    JavaScript:学习笔记(6)——New运算符 new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例. 快速开始 当你使用new关键字的时候,会 创建一个新的对象 将th ...

  3. 前端 初级篇(HTML)

    HTML 概述: HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,他是一种制作万维网页面标准语言(标记).相当于定义统一的一套规则,大家都来遵守他,这样就 ...

  4. matlab fread

    Matlab中fread函数用法    “fread”以二进制形式,从文件读出数据. 语法1:[a,count]=fread(fid,size,precision) 语法2:[a,count]=fre ...

  5. 查看linux系统版本信息(Oracle Linux、Centos Linux、Redhat Linux、Debian、Ubuntu)

    一.查看Linux系统版本的命令(3种方法) 1.cat /etc/issue,此命令也适用于所有的Linux发行版. [root@S-CentOS home]# cat /etc/issue Cen ...

  6. awk的内置函数

    常见awk内置数值函数

  7. Java开发者或许应该经常去看看的网站?...

    Java开发者或许应该经常去看看的网站?...Google top3 1.Oracle Technology Network for Java Developers | Oracle Technolo ...

  8. Device Tree(一):背景介绍【转】

    本文转载自:http://www.wowotech.net/device_model/why-dt.html 一.前言 作为一个多年耕耘在linux 2.6.23内核的开发者,各个不同项目中各种不同周 ...

  9. Eclipse Task的使用

    参考链接:http://blog.csdn.net/limb99/article/details/8881891; http://hi.baidu.com/jinxv1987/item/64496f6 ...

  10. Optional int parameter 'time' is present but cannot be translated into a null value due to being decla

    今天在操作redis的时候报了这个错:Optional int parameter 'time' is present but cannot be translated into a null val ...