<题解>[IOI2019]景点划分
这个题对我来说可以算是超出了我的能力范围
被学长拿来教我做构造,构造题真简单,构造题真是人,构造题真能手切。。。
首先对于本题,必须要知道dfs树这东西,就是在一个图中得到一个树,
简单来说就是吧所有的边分成树边和非树边,所有的n-1个树边会把所有点连接成一颗树
这个在实现上就是一个dfs就好了
这里有一个小小的性质,在一个dfs树中,所有的非树边一定不是横叉边
意思就是所有的边连接的两个点一定是祖先后代关系
那么这个题就可以做了
你会发现这a,b,c并没有特殊的要求,所以我们选择这其中较小的那两个一定是优的
所以我们直接扔掉最大的那个,这时候你会发现
为了保证我们很顺利的找到答案,我们必须要找到树的重心,
因为在重心两侧,子树的大小一定小于等于\(\frac{n}{2}\),而a,b的大小又小于等于\(\frac{n}{3}\)
这样的话,我们只要保存这个重心,我们可以任意连接子树,从而一定可以满足\(a,b\)的其中一个
而剩下的子树中的节点数又一定大于a,这样为我们找到答案提供了很大的方便
那么我们一定先满足\(b\),因为我们要是想凑出a来一定是比b更容易,那么我们接下来的重点就是要找a
首先我们看\(m=n-1\)的情况,这样的话本来就是一棵树,直接找到重心,判断是不是有子树的大小大于a,如果有 ,那么就有解,没有的话,就无解呗
因为你没有别的边可以连接另外的子树,所以你当前的子树就你你可以找到的所有可以作为a的联通块
而b可以通过重心连接,不需要考虑
那么这时候你会发现,如果是图的话,那就可以有别的边来连接??虽然不可以连接在dfs树上的子树,
但是别忘了,在dfs树上,它的祖先也是他的子树,所以我们只要不断的去连接和它祖先有连边的子树,并且将大小加和
如果能够得到一个大小为a的联通块,那就有解,当然这时候你并没有选重心,b仍然是合法的
我们来看这里的重心有什么用,这个重心首先保证了你有一堆大小\(<=\frac{n}{2}\)的子树
这样的话,你一定可以凑出一个b来,而重心又在一定程度上帮你将整张图分为两部分
一个是那一堆子树,一个是祖先那边的节点,这就是构造的意义所在
构造你想要的条件来解决一般问题
还有一些要注意的情况,
有可能在图中,你并不能找到合法的反祖边,但是呢,有一颗子树的大小是合法的,
这个时候就要向树的做法一样了,我要去判断一下每一颗子树的大小
还有输出的时候,千万不要找到一颗子树就开始从他的根节点遍历
因为你是从有和祖先连边的那个点出发才可以联通的啊,要从他开始
我的代码中imp这个数组就是干这个活的
AC_code
#include <bits/stdc++.h>
using namespace std;
#define re register int
const int N = 1e5 + 5;
const int M = 2e5 + 5;
int n, m;
pair<int, int> a[4];
int fr[M * 2], to[M * 2], nxt[M * 2], head[N], rp = 1;
void add_edg(int x, int y) {
to[++rp] = y;
fr[rp] = x;
nxt[rp] = head[x];
head[x] = rp;
}
int fa[N], dep[N], siz[N], rt, mn = 0x3f3f3f3f;
bool vis[N], pd[M * 2];
void dfs(int x) {
vis[x] = true;
siz[x] = 1;
int mx = 0;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y])
continue;
fa[y] = x;
pd[i] = pd[i ^ 1] = true;
dep[y] = dep[x] + 1;
dfs(y);
siz[x] += siz[y];
if (siz[y] > mx)
mx = siz[y];
}
if (n - siz[x] > mx)
mx = n - siz[x];
if (mx < mn)
mn = mx, rt = x;
}
int imp[N], bl[N], sz[N];
void change(int x, int b) {
bl[x] = b;
sz[b]++;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (dep[y] != dep[x] + 1)
continue;
change(y, b);
}
}
int ji[N], cnt;
int ans[N];
void biao1(int x) {
if (cnt == a[1].first)
return ;
ans[x] = a[1].second;
cnt++;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == rt || dep[y] != dep[x] + 1)
continue;
biao1(y);
}
}
void biao2(int x, int f) {
if (cnt == a[1].first || ans[x])
return ;
ans[x] = a[1].second;
cnt++;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (bl[y] != f || ans[y])
continue;
biao2(y, f);
}
}
void biao3(int x) {
if (cnt == a[2].first)
return ;
ans[x] = a[2].second;
cnt++;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (dep[y] != dep[x] + 1)
continue;
biao3(y);
}
}
signed main() {
scanf("%d%d", &n, &m);
scanf("%d%d%d", &a[1].first, &a[2].first, &a[3].first);
a[1].second = 1;
a[2].second = 2;
a[3].second = 3;
sort(a + 1, a + 4);
for (re i = 1, x, y; i <= m; i++) {
scanf("%d%d", &x, &y);
x++;
y++;
add_edg(x, y);
add_edg(y, x);
}
dfs(1);
siz[0] = n - 1;
//cout<<rt<<endl;
for (re i = head[rt]; i; i = nxt[i]) {
int y = to[i];
if (y == fa[rt] || dep[y] != dep[rt] + 1)
continue;
change(y, y);
//cout<<rt<<" "<<y<<" "<<siz[0]<<" "<<siz[y]<<endl;
siz[0] -= sz[y];
}
//cout<<rt<<" "<<siz[rt]<<" "<<siz[0]<<endl;
for (re i = 2; i <= rp; i += 2) {
if (pd[i])
continue;
//cout<<"sb"<<endl;
if (siz[0] >= a[1].first)
break;
int u = fr[i], v = to[i];
//cout<<bl[u]<<" "<<bl[v]<<endl;
if (bl[u] && bl[v])
continue;
if (!bl[u] && !bl[v])
continue;
//cout<<u<<" "<<v<<" "<<siz[bl[u]]<<" "<<siz[bl[v]]<<endl;
if (bl[u] && !ji[bl[u]] && (v != rt || siz[0] == 0)) {
ji[bl[u]] = 1;
imp[bl[u]] = u;
siz[0] += sz[bl[u]];
}
if (bl[v] && !ji[bl[v]] && (u != rt || siz[0] == 0)) {
ji[bl[v]] = 1;
imp[bl[v]] = v;
siz[0] += sz[bl[v]];
}
}
//cout<<siz[0]<<endl;
int flag = 0;
if (siz[0] < a[1].first) {
for (re i = head[rt]; i; i = nxt[i]) {
int y = to[i];
if (dep[y] != dep[rt] + 1)
continue;
if (siz[y] >= a[1].first) {
siz[0] = siz[y];
ji[y] = 1;
imp[y] = y;
flag = 1;
break;
}
}
}
if (siz[0] >= a[1].first) {
cnt = 0;
if ((rt != 1 && !flag))
biao1(1);
for (re i = head[rt]; i; i = nxt[i]) {
int y = to[i];
if (!ji[y] || dep[y] != dep[rt] + 1)
continue;
if (cnt >= a[1].first)
break;
biao2(imp[y], y); //cout<<siz[y]<<endl;
}
cnt = 1;
ans[rt] = a[2].second;
for (re i = head[rt]; i; i = nxt[i]) {
int y = to[i];
if (ji[y] || dep[y] != dep[rt] + 1)
continue;
if (cnt >= a[2].first)
break;
biao3(y);
}
}
//cout<<a[1].second<<endl;
for (re i = 1; i <= n; i++) {
if (siz[0] >= a[1].first && !ans[i])
ans[i] = a[3].second;
printf("%d ", ans[i]);
}
//cout<<p<<endl;
}
<题解>[IOI2019]景点划分的更多相关文章
- 【题解】整数划分 [51nod1201] 整数划分 V2 [51nod1259]
[题解]整数划分 [51nod1201] 整数划分 V2 [51nod1259] 传送门:整数划分 \([51nod1201]\) 整数划分 \(V2\) \([51nod1259]\)** [题目描 ...
- 题解 [51nod1201] 整数划分
题面 解析 首先,因为是不同的数字, 可以从小到大依次枚举加上每一个数字的贡献,再枚举每个数. 然而这样会T掉... 考虑到\(n\)只有\(50000\), 当分成的数最多时,设最大的数为\(m\) ...
- [loj3176]景点划分
不妨设$a\le b\le c$,那么相当于要找到两个大小至少为$a$和$b$的连通块(连通块可以通过删除度最小的点变小) 以一个点为根建出dfs树并对以下情况分类讨论: 1.存在一个节点满足$\ma ...
- world.construct(me);
目录 0 引言 0.1 所谓构造题 0.2 重点是动机 (motivation) 1 实践出真知 1.1 「CSP-S 2021」「洛谷 P7915」回文 1.1.1 题目大意 1.1.2 解题过程 ...
- HDU 2665 Kth number(可持续化线段树)
Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- HDU 3966 Aragorn's Story 树链剖分+BIT区间修改/单点询问
Aragorn's Story Description Our protagonist is the handsome human prince Aragorn comes from The Lord ...
- 题解 洛谷 P4047 【[JSOI2010]部落划分】
我觉得几乎就是一道最小生成树模板啊... 题解里许多大佬都说选第n-k+1条边,可我觉得要这么讲比较容易理解 (虚边为能选的边,实边为最小生成树) 令n=5,k=2,(1,3)<(1,2)< ...
- 洛谷P4047 [JSOI2010]部落划分题解
洛谷P4047 [JSOI2010]部落划分题解 题目描述 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落 ...
- CF1238E.Keyboard Purchase 题解 状压/子集划分DP
作者:zifeiy 标签:状压DP,子集划分DP 题目链接:https://codeforces.com/contest/1238/problem/E 题目大意: 给你一个长度为 \(n(n \le ...
随机推荐
- Java双重循环
在实际开发中我们常常遇到这样的问题,有A.B两个集合,这两个集合的某一个字段是相同的,要把A集合和B进行匹配,然后把A的值赋值给B例如: //上传图片 List<MultipartFile> ...
- 【模板】Tarjian求LCA
概念 公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点 举个例子吧,如下图所示4和5的最近公共祖先是2,5和3的最近公共祖先是1,2和1的最近公共祖先是1. 算法 常用的求LCA的算法有:Ta ...
- 约会Rendezvous
约会 Rendezvous 内存限制:128 MiB 时间限制:1000 ms 标准输入输出 题目描述 给定一个有 nnn 个顶点的有向图,每个顶点有且仅有一条出边.每次询问给出两个顶点 ai ...
- google protobuf的原理和思路提炼
之前其实已经用了5篇文章完整地分析了protobuf的原理.回过头去看,感觉一方面篇幅过大,另一方面过于追求细节和源码,对protobuf的初学者并不十分友好,因此这篇文章将会站在"了解.使 ...
- 免费获取思维导图Mindmaster会员教程
免费获取思维导图Mindmaster会员教程 步骤一.下载安装 搜索亿图官方网页,下载安装免费版Mindmaster思维导图. 步骤二.点击登录 打开Mindmaster思维导图,点击登录,可以使用微 ...
- VRRP协议与原理
VRRP协议与原理 目录: 一.VRRP协议概述 1.1.VRRP协议 1.2.单网关和多网关的缺陷 1.3.VRRP基本概述 二.VRRP工作原理 2.1.VRRP主备份备份工作工程 2.2.VRR ...
- .net core 支付宝,微信支付 一
源码: https://github.com/aspros-luo/Qwerty.Payment/tree/develop 支付宝支付:参考支付宝sdk及文档,https://docs.open.al ...
- 10、mysql增量恢复实战
10.1.实验数据: mysql> select * from test; +----+------+------+ | id | name | age | +----+------+----- ...
- java list 分页
/** * * @param list * @param pageNum * @param pageSize * @param <T> * @return 返回当前页数据 */ publi ...
- POJ 1269 Intersecting Lines 判断两直线关系
用的是初中学的方法 #include <iostream> #include <cstdio> #include <cstring> #include <al ...