COCI2014/2015 Contest#1 D MAFIJA【基环树最大独立点集】
T1725 天黑请闭眼
Online Judge:COCI2014/2015 Contest#1 D MAFIJA(原题)
Label:基环树,断环+树形Dp,贪心+拓扑
题目描述
最近天黑请闭眼在 C国十分流行!游戏里有两个身份,一个是杀手,另一个是平民。杀手知道哪些人是杀手,而平民对此一无所知。现在为了知道谁是杀手,参与游戏的每个人都指证了一个人为杀手,可以确定的是,杀手一定会指证平民,而平民指证的人有可能是杀手,也有可能是平民。给出每位玩家指证的人,请找出游戏中最多可能的杀手个数。
输入
第一行包括一个整数N,表示玩家个数.玩家分别被编号为1~N.
接下来N行,每行一个整数,其中第K行的数表示编号为K的玩家所指证为杀手的玩家编号。
对每组测试点输出一行,如果满足条件输出Yes否则输出No。
输出
输出仅一行,表示最多可能的杀手个数。
样例
Input
7
3
3
4
5
6
4
4
3
2
3
1
3
2
1
1
Output
4
1
2
Hint
100%的数据 N<=500000.
题解
虽然题目给的是有向边,但其实相当于双向边。且对于边\((u,v)\),只需要保证两者间至多只有一个杀手。
证明:
对于有向边u->v,根据题意只存在以下取法\((u=1,v=0),(u=0,v=1),(u=0,v=0)\);
对于有向边v->u,类似的只存在以下取法\((v=0,u=1),(v=0,u=0),(v=1,u=0)\)
整理分析得,不论该边方向如何,相连的两点间最多只能取1个杀手。
题目就变成了求基环树上的最大独立集。
做法一
联想起一道类似的经典树形Dp题目——没有上司的舞会.
相比,这道题是一棵基环树,多了一个环,而且图也可能不连通。但有一个重要的发现:每个连通的块内,都有且仅有一个环。
考虑基环树的常见套路——断环,然后转为树形问题解决。
Q1:怎么断环?
利用并查集维护点与点的连通情况,把合并前就已经在同一集合的边记录下来。到时候dfs时,避开这条边即可。
Q2:断环后怎么搞?
断环后就形成了一棵普通的树,直接树形Dp即可(就是上面那道的做法)。但注意,我们记录的断掉的边\((u,v)\)这两者不能同时取杀手。如何较方便的解决?直接分别以\(u,v\)为树根dfs两遍即可,最后将\(max(dp[u][0],dp[v][0])\)累计到答案(因为可能会有多个连通的块)。
整个算法的时间复杂度为\(O(N)\)。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
inline int read(){
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
struct edge{
int to,nxt;
}e[2*N];
int n,head[N],cnt;
inline void link(int u,int v){
e[++cnt].to=v,e[cnt].nxt=head[u];
head[u]=cnt;
}
int tot,fa[N],u[N],v[N],id[N];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int dp[N][2];
void dfs(int x,int f,int ban){
dp[x][0]=0,dp[x][1]=1;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==f||i==ban||i==ban-1)continue;
dfs(y,x,ban);
dp[x][0]+=max(dp[y][0],dp[y][1]);
dp[x][1]+=dp[y][0];
}
}
int main(){
n=read();
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=n;i++){
int to=read();
link(i,to);link(to,i);
int A=find(i),B=find(to);
if(A==B){//去除环上的某条边
u[++tot]=i,v[tot]=to,id[tot]=2*i;
}
else fa[A]=B;
}
int ans=0;
for(int i=1;i<=tot;i++){
dfs(u[i],0,id[i]);
int tmp1=dp[u[i]][0];
dfs(v[i],0,id[i]);
int tmp2=dp[v[i]][0];
ans+=max(tmp1,tmp2);
}
printf("%d\n",ans);
}
做法二
关于求基环树(也适用于普通树)上的最大独立点集(不带权值),有这样一个贪心方案。
对于某个入度为0的点\(x\)(这里我们重新将边视作单向边),它指向\(to[x]\),那么显然是将\(to[x]\)染黑较优,所以可以用类似拓扑的dfs去给点打标记,最后计个数。
#include<bits/stdc++.h>
using namespace std;
const int N=500010;
int n,ans=0,to[N],ing[N];
bool mark[N];
void dfs(int x,int w){
if(mark[x])return;
mark[x]=1;
if(w)ans++;
ing[to[x]]--;
if(ing[to[x]]==0||w==1)dfs(to[x],!w);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&to[i]),ing[to[i]]++;
for(int i=1;i<=n;i++)if(!ing[i])dfs(i,1);
for(int i=1;i<=n;i++)if(!mark[i])dfs(i,0);
printf("%d\n",ans);
}
两者的时间复杂度都是\(O(N)\)的,但相较之下第一种断环+树形dp的做法更具推广性,适用于点带权的情况——比如下面几道题:
COCI2014/2015 Contest#1 D MAFIJA【基环树最大独立点集】的更多相关文章
- 2018-2-6考试(COCI2014/2015 Contest#5)
T1:FUNGHI(1s,32M,50pts)得分:50 题意:给你8个数组成一个环,要你求出其中连续的4个数,让它们的和最大 题解:暴力求出每一连续4个数之和,比较一下就好 标签:模拟 C++ Co ...
- C++算法代码——求数列[coci2014/2015 contest #1]
题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1815 题目描述 Mirko在数学课上以一种有趣的方式操作数列,首先,他写下一个数列A ...
- luogu3651 展翅翱翔之时 (はばたきのとき)[基环树+贪心]
考前随便做点水题愉♂悦身心 有助于退役 这题意思其实就是说要把外向基环树森林改成一个环的最小代价. 依照套路,先对每棵基环树的树做dp,这里因为要是环,要把所有的树都拆成链,然后连接.所以考虑以最小代 ...
- 【BZOJ1791】【IOI2008】【基环树】island(status第一速度)
1791: [Ioi2008]Island 岛屿 Time Limit: 20 Sec Memory Limit: 162 MB Submit: 908 Solved: 159 [Su ...
- 『Island 基环树直径』
Island(IOI 2008) Description 你准备浏览一个公园,该公园由 N 个岛屿组成,当地管理部门从每个岛屿 i 出发向另外一个岛屿建了一座长度为 L_i 的桥,不过桥是可以双向行走 ...
- 【BZOJ4883】 [Lydsy1705月赛]棋盘上的守卫(最小生成树,基环树)
传送门 BZOJ Solution 考虑一下如果把行,列当成点,那么显然这个东西就是一个基环树对吧. 直接按照\(Kruscal\)那样子搞就好了. 代码实现 代码戳这里
- [Codeforces235D]Graph Game——概率与期望+基环树+容斥
题目链接: Codeforces235D 题目大意:给出一棵基环树,并给出如下点分治过程,求点数总遍历次数的期望. 点分治过程: 1.遍历当前联通块内所有点 2.随机选择联通块内一个点删除掉 3.对新 ...
- 洛谷AT2046 Namori(思维,基环树,树形DP)
洛谷题目传送门 神仙思维题还是要写点东西才好. 树 每次操作把相邻且同色的点反色,直接这样思考会发现状态有很强的后效性,没办法考虑转移. 因为树是二分图,所以我们转化模型:在树的奇数层的所有点上都有一 ...
- bzoj1791[IOI2008]Island岛屿(基环树+DP)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和.n< ...
随机推荐
- CSIC_716_20191126【面向对象编程--继承】
继承 什么是继承:继承是新建类的一种方式,通过此方式生成的类称为子类.或者 派生类,被继承的类称为父类.基类或超类.在python中,一个子类可以继承多个父类. 继承的作用:减少代码的冗余,提高开发效 ...
- Buy Low, Buy Lower
Buy Low, Buy Lower 给出一个长度为N序列\(\{a_i\}\),询问最长的严格下降子序列,以及这样的序列的个数,\(1 <= N <= 5000\). 解 显然我们可以很 ...
- [JZOJ3234] 阴阳
题目 题目大意 有一棵树,每条边有\(0\)或\(1\)两种颜色. 求满足存在\((u,v)\)路径上的点\(x\)使得\((u,x)\)和\((x,v)\)路径上的两种颜色出现次数相同的点对\((u ...
- C++和JavaScript脚本的相互调用
脚本调用C++相对比较容易,使用ATL组件只需要抛双接口即可,但在exe里如何做到呢?本文实现了在exe里脚本和C++的相互调用.在EXE里也需要对外抛送一个继承自IDispatch的接口.并需要重载 ...
- iOS开发之SceneKit框架--SCNCamera.h
1.SCNCamera简介 被称为照相机或者摄像机,可以附加到节点以提供显示场景的角度.其实就是用户视角和人的眼睛一样. 2.相关API简介 初始化 //懒加载 + (instancetype)cam ...
- spring boot过滤器FilterRegistrationBean
有2种方式可以实现过滤器 1:通过FilterRegistrationBean实例注册 2:通过@WebFilter注解生效 这里选择第一种,因为第二种不能设置过滤器之间的优先级 为了演示优先级,这里 ...
- <剑指offer>面试题
题目1:二维数组的查找 题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断 ...
- <每日一题>题目12:列表解析及zip、dict函数的简单应用
''' 分析: 1.列表解析:迭代机制的一种应用 语法: [expression for iter_val in iterable] [expression for iter_val in itera ...
- selenium基础(控制浏览器)
python基础(控制浏览器) 控制浏览器 控制浏览器窗口大小 设置浏览器屏幕大小方法:set_window_size() 浏览器最大化:maximize_window() 浏览器最小化:minimi ...
- 解决asp.net web api时间datetime自动带上带上的T和毫秒的问题
今天用asp.net web api写微信小程序的接口时遇到一个问题. 返回的model中的datetime类型的字段自动转换成了“2014-11-08T01:50:06:234”这样的字符串,带上的 ...