【gdgzezoi】Problem C: The Winds of Winter
Description
给定一棵n个点的树,对于树上每个结点,将它删去,然后可以将得到的森林中任意一个点与其父亲断开并连接到另一颗树上,对每一个点求出森林中所有树size最大值的最小值。
Input
第一行,一个数n。
第2~n+1行,每行两个整数x,y,(1<=x<=y<=n),表示x是y的父亲(若x=0则y为根)。
Output
共n行,每行一个数,第i行表示删去点i后的答案。
Sample Input
10
0 1
1 2
1 3
1 4
2 5
2 6
3 7
4 8
4 9
5 10
Sample Output
3
4
5
5
5
9
9
9
9
9
Hint
10%的数据,n<=10
30%的数据,n<=10^3
100%的数据,n<=10^5
思路
先考虑怎么求一个点的答案。令size[i]表示点i的子树和,min表示森林中最小的树的大小,max表示森林中最大的树的大小。那么我们可以二分答案ans,问题就转化为最大的树中是否存在一个点i,使size[i]+min≤ans且max−size[i]≤ans,即max−ans≤size[i]≤ans−min。
注意二分的边界。
从根开始dfs一遍,维护4个map:
mappath:从根到点i父亲所有点的size
mapheaviest:点i重儿子的子树中所有点的size
maplight:点i所有轻儿子的子树中所有点的size
mapother:除上述map中所有点之外其它点的size
求点i的答案时,判断删去这个点后森林中最大的树是什么:
如果是点i的重儿子,直接二分,在mapheaviest上查询即可求出答案。
如果是根,那么由于在从根到点i父亲路径上所有点size在删去i后要减去size[i],要特判一下。其他可以在mapother上查询。
接下来考虑怎么维护这4个map:
mappath:dfs时更新,退出时删除即可。
mapheaviset , maplight:可以用树上启发式合并求。
mapother:初始时所有点都在内,其他map中加入一个点时就删除这个点。
时间复杂度O(nlogn)
代码
#include<bits/stdc++.h>
using namespace std;
#define it map<int,int>::iterator
map<int,int> mp[4];
vector<int> v[100010];
int size[100010],son[100010],mn[100010],mx2[100010],ans[100010],n,m,rt;
void add(int x,int y,int hh)
{mp[hh][x]+=y;mp[3][x]-=y;if (!mp[3][x]) mp[3].erase(x);if (!mp[hh][x]) mp[hh].erase(x);
}void insert(int x,int y,int hh)
{add(size[x],y,hh);for (int i=0;i<v[x].size();i++) insert(v[x][i],y,hh);
}int find(int x,int y,int l,int r,int hh,int z)
{if (x==y&&x>=l&&x<=r) return x;while (l<=r){int mid=(l+r)>>1;it t=mp[hh].lower_bound(y-mid+z);if (t!=mp[hh].end()&&t->first<=mid-x+z) r=mid-1;else l=mid+1;}return l;
}void work(int x)
{if (size[x]==n) ans[x]=find(mn[x],size[son[x]],mx2[x],size[son[x]],1,0);else if (size[son[x]]>n-size[x]) ans[x]=find(min(mn[x],n-size[x]),size[son[x]],max(n-size[x],mx2[x]),size[son[x]],1,0);else if (!son[x]) ans[x]=n-size[x];else ans[x]=min(find(mn[x],n-size[x],size[son[x]],n-size[x],0,size[x]),find(mn[x],n-size[x],size[son[x]],n-size[x],3,0));
}void pre(int x)
{size[x]=1;mn[x]=n;for (int i=0;i<v[x].size();i++){pre(v[x][i]);size[x]+=size[v[x][i]];mn[x]=min(mn[x],size[v[x][i]]);if (size[v[x][i]]>size[son[x]]) mx2[x]=size[son[x]],son[x]=v[x][i];else if (size[v[x][i]]>mx2[x]) mx2[x]=size[v[x][i]];}mp[3][size[x]]++;if (size[x]<n) mn[x]=min(mn[x],n-size[x]);
}void dfs(int x,bool bo)
{add(size[x],1,0);for (int i=0;i<v[x].size();i++) if (v[x][i]!=son[x]) dfs(v[x][i],0);if (son[x]) dfs(son[x],1);for (int i=0;i<v[x].size();i++) if (v[x][i]!=son[x]) insert(v[x][i],1,2);if (!(--mp[0][size[x]])) mp[0].erase(size[x]);work(x);mp[3][size[x]]++;if (bo){add(size[x],1,1);for (it t=mp[2].begin();t!=mp[2].end();){mp[1][t->first]+=t->second;mp[2].erase(t++);}}else for (int i=0;i<v[x].size();i++){int t=v[x][i];if (t!=son[x]) insert(t,-1,2);else insert(t,-1,1);}
}int main()
{scanf("%d",&n);if (n==1) { puts("0");return 0; }for (int i=1;i<=n;i++){int x,y;scanf("%d%d",&x,&y);if (x) v[x].push_back(y);else rt=y;}pre(rt);dfs(rt,1);for (int i=1;i<=n;i++) printf("%d\n",ans[i]);return 0;
}
【gdgzezoi】Problem C: The Winds of Winter相关推荐
- 【gdgzezoi】Problem A: Fairy
Description 给定n个点,m条边的无向图(无自环),可以从图中删除一条边,问删除哪些边可以使图变成一个二分图. Input 第1行包含两个整数n,m,分别表示点数和边数. 第2~m+1行每行 ...
- 【gdgzezoi】Problem B: 天天爱跑步
Description 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图 ...
- 【gdgzezoi】Problem A: 玩具谜题
Description 小南有一套可爱的玩具小人,它们各有不同的职业. 有一天,这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外.如下图: 这时 sin ...
- 【FZU】Problem 2181 快来买肉松饼 点双连通
传送门:[FZU]Problem 2181 快来买肉松饼 题目分析:无向图找奇圈的问题.首先我们做tarjan求出点双连通块,每一个块中用黑白染色法得到最长的奇圈,然后这个奇圈中不参加游戏的小孩就是这 ...
- 【FZU】Problem 2217 Taxi 【暴力状压dp】
传送门:[FZU]Problem 2217 Taxi my code: my~~~code: ///* #include <stdio.h> #include <string.h ...
- 【动态规划】Problem 4 聪明伶俐的香穗子
Problem 4 聪明伶俐的香穗子 香穗子遇到难题了. 题目是这样的,一个序列上有n个整数,现在你要取m个,且这m个数的任意两个不能相隔的太近,否则这样会太丑,现在问你最大能得到多大的和 输入: 第 ...
- 【FOJ】Problem 1683 纪念SlingShot
Problem 1683 纪念SlingShot. 思路 构造矩阵T S(n) = Tn-2[0][0] × S(2) + Tn-2[0][1] × f(2) + Tn-2[0][2] × f(1) ...
- 【FOJ】Problem 1489 密码
Problem 1489 密码. 思路 存数组,读入得如果是字母返回对应密文表的字母 小写的再转一下小写 笔记 千万记得给字符数组附初值!!! 代码 #include<cstdio> #i ...
- 【FOJ】Problem 1077 铁皮容器
Problem 1077 铁皮容器. 题意 圆柱,有盖,表面积1000平方厘米,已知容积(体积),求最小底面半径(小数点后一位),无解输出NO 思路 这是数学题??? 列公式化简:V=500r-πr3 ...
最新文章
- YII1 MVC初认识(二)
- 【NLP】文本相似度的BERT度量方法
- eclipse中YAML文件编辑插件:Yaml Editor插件安装
- 三条中线分的六个三角形_八年级数学上册:三角形已知两条边如何求第三边
- 移植wpa_supplicant 2.2问题
- 王道操作系统考研笔记——2.3.3 进程互斥的硬件实现方法
- python的类方法_python 类不实例化,调用类方法:@staticmethod 和 @classmethod
- lingo与matlab部分常用函数总结
- python牛客网编程题_一波优秀的自学编程语言网站
- curl: (7) Failed to connect to raw.githubusercontent.com port 443: Connection refused
- jTopo 功能完善
- sqlserver:什么是数据库实例?
- 平衡二叉树详解 通俗易懂
- 关于Pidgin和webqq
- 华为服务器网口ip配置文件,华为服务器、存储IP初始配置方法
- excel拆分表格之按指定行数拆分
- 高性能浏览器网络(High Performance Browser Networking) 第四章
- 什么是Session 如何使用Session
- 软件性能测试方案模板,软件系统 性能设计方案 软件系统设计方案模板
- 全面讲解Web3.0域名的应用场景-赛道情况-未来挑战