【2020.11.30提高组模拟】剪辣椒(chilli) 题解
【2020.11.30提高组模拟】剪辣椒(chilli) 题解
题意简述
给你一棵树,删去两条边,使得形成的三棵新树的大小极差最小化。求最小极差.
\(3\le n\le 200,000\).
Solution
假设根为\(1\).
设删去的点意为这个点到其父亲的连边被删了,关系指一个点是另一个点的祖先,\(size_x\)为以\(x\)为根的子树大小,\(a,b,c\)是三棵新子树的大小。
答案即为
\]
那么只会有两种情况:
被删去的点有关系
不妨设\(x\)是\(y\)的祖先
\]
被删去的点无关系
\]
显然这里就有一种比较暴力的方法了,时间复杂度\(O(n^2)\)。
考虑优化。
optimization
在\(dfs\)中,我们维护两个\(set:A,B\)
当进入点\(x\)时,把\(size_x\)放入\(A\)中;
当走出点\(x\)时,把\(size_x\)从\(A\)中删除并放入\(B\)中;
这样\(A\)里的元素就是\(x\)的直接祖先,\(B\)中是与\(x\)无关系的点(不需要考虑到还没有访问到的点,因为反过来会遍历到的)
接下来每到一个点\(x\),我们继续考虑两种情况:
被删去的点有关系
假如我们要删去点\(x\),另一个被删去的点为y应使得\(n-size_y\)和\(size_y-size_x\)这两个子树的大小最平均,即\(n-size_y=size_y-size_x\Rightarrow 2size_y=n+size_x\)。

我承认我图很丑,我谢罪……
那么我们就去\(A\)中找一个值接近上面\(size_y\)的值并更新答案。
当然,不一定会有正好相等的,应该用lower_bound()后在让指针iter--,进行两次判断。
被删去的点无关系
同理假如我们要删去点\(x\)和与它无关系的点\(y\),就要使得\(size_y\)和\(n-size_x-size_y\)这两个值最相近,即\(size_y=n-size_x-size_y\Rightarrow 2size_y=n-size_x\)。

我承认我图很丑,我再次谢罪……
那么我们就去\(B\)中找一个值接近上面\(size_y\)的值并更新答案。
Code
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#ifdef TH
#define debug printf("Now is %d\n",__LINE__);
#else
#define debug
#endif
using namespace std;
template<class T>inline void read(T&x)
{
char ch=getchar();
int fu;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') fu=-1,ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
x*=fu;
}
inline int read()
{
int x=0,fu=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') fu=-1,ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
int g=0;
if(x<0) x=-x,putchar('-');
do{G[++g]=x%10;x/=10;}while(x);
for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int n;
int ans=100000000;
set<int>A,B;
int sze[200010];
int head[400010],ver[400010],nxt[400010];
int cnt;
void insert(int x,int y)
{
nxt[++cnt]=head[x];
ver[cnt]=y;
head[x]=cnt;
nxt[++cnt]=head[y];
ver[cnt]=x;
head[y]=cnt;
}
void dfs1(int x,int fa)
{
sze[x]=1;
for(int i=head[x];i;i=nxt[i])
{
if(ver[i]!=fa)
{
dfs1(ver[i],x);
sze[x]+=sze[ver[i]];
}
}
}
IL void upd(int x,int y,int z)
{
ans=min(ans,max({x,y,z})-min({x,y,z}));
}
void dfs2(int x,int fa)
{
// cout<<sze[x]<<endl;
A.insert(sze[x]);
set<int>::iterator iter;
iter=A.upper_bound((sze[x]+n)/2);
if(iter!=A.end())
{
upd(sze[x],*iter-sze[x],n-*iter);
}
if(iter!=A.begin())
{
iter--;
upd(sze[x],*iter-sze[x],n-*iter);
}
iter=B.upper_bound((n-sze[x])/2);
if(iter!=B.end())
{
upd(sze[x],*iter,n-sze[x]-*iter);
}
if(iter!=B.begin())
{
iter--;
upd(sze[x],*iter,n-sze[x]-*iter);
}
for(int i=head[x];i;i=nxt[i])
{
if(ver[i]!=fa)
{
dfs2(ver[i],x);
}
}
A.erase(sze[x]);
B.insert(sze[x]);
}
int main()
{
// freopen("chilli.in","r",stdin);
// freopen("chilli.out","w",stdout);
n=read();
for(int i=1;i<n;i++) insert(read(),read());
dfs1(1,0);
dfs2(1,0);
cout<<ans;
return 0;
}
End
一次性写完代码运行一下,没仔细看就以为自己WA了就来写Solution。写完Solution准备来Debug,写完的Debug语句再运行发现代码是对的!Woc泪目!
绝了,\(std\)是老外写的吧,全是c++11,auto啥的
所以我也用了初始化列表+max(lists)
嘿嘿,还挺方便的呢
我在跑操的时候突然发现,这道题有些平衡树的思想。(而不是把\(set\)当成离散化bool数组来用)
不断的插入值,删除值,求前驱后继……不就是平衡树嘛?
看来我需要加强模型转化能力。
【2020.11.30提高组模拟】剪辣椒(chilli) 题解的更多相关文章
- 【2020.11.30提高组模拟】剪辣椒(chilli)
剪辣椒(chilli) 题目描述 在花园里劳累了一上午之后,你决定用自己种的干辣椒奖励自己. 你有n个辣椒,这些辣椒用n-1条绳子连接在一起,任意两个辣椒通过用若干个绳子相连,即形成一棵树. 你决定分 ...
- JZOJ 【2020.11.30提高组模拟】剪辣椒(chilli)
题目大意 给出一棵 \(n\) 个节点的树,删去其中两条边 使得分出的三个子树大小中最大与最小的差最小 分析 先一边 \(dfs\) 预处理出以 \(1\) 为根每个点的 \(size\) 然后按 \ ...
- 【2020.11.30提高组模拟】删边(delete)
删边(delete) 题目 题目描述 给你一棵n个结点的树,每个结点有一个权值,删除一条边的费用为该边连接的两个子树中结点权值最大值之和.现要删除树中的所有边,删除边的顺序可以任意设定,请计算出所有方 ...
- 【2020.11.28提高组模拟】T1染色(color)
[2020.11.28提高组模拟]T1染色(color) 题目 题目描述 给定 \(n\),你现在需要给整数 \(1\) 到 \(n\) 进行染色,使得对于所有的 \(1\leq i<j\leq ...
- 【2020.11.28提高组模拟】T2 序列(array)
序列(array) 题目描述 给定一个长为 \(m\) 的序列 \(a\). 有一个长为 \(m\) 的序列 \(b\),需满足 \(0\leq b_i \leq n\),\(\sum_{i=1}^ ...
- JZOJ 6904. 【2020.11.28提高组模拟】T3 树上询问(query)
题目 你有一棵 \(n\) 节点的树 ,回答 \(m\) 个询问,每次询问给你两个整数 \(l,r\) ,问存在多少个整数 \(k\) 使得从 \(l\) 沿着 \(l \to r\) 的简单路径走 ...
- 【2020.12.03提高组模拟】A组反思
估计:40+10+0+0=50 实际:40+10+0+0=50 rank40 T1 赛时看到\(n,m\leq9\),我当机立断决定打表,暴力打了几个点之后发现在\(n\ne m\)且\(k\ne0\ ...
- 11.5NOIP2018提高组模拟题
书信(letter) Description 有 n 个小朋友, 编号为 1 到 n, 他们每人写了一封信, 放到了一个信箱里, 接下来每个人从中抽取一封书信. 显然, 这样一共有 n!种拿到书信的情 ...
- 【2020.12.01提高组模拟】卡特兰数(catalan)
题目 题目描述 今天,接触信息学不久的小\(A\)刚刚学习了卡特兰数. 卡特兰数的一个经典定义是,将\(n\)个数依次入栈,合法的出栈序列个数. 小\(A\)觉得这样的情况太平凡了.于是,他给出了\( ...
- 【2020.12.01提高组模拟】A组反思
105,rk45 T1 赛时一开始先打了\(m=0\)的情况,也就是普通的卡特兰数,然后打了暴力,样例过了,把样例改改就不行了,原因没有保证是枚举的是合法的出栈序列 得分:\(WA\&TLE1 ...
随机推荐
- C#开发手册
一. 编码规范 (一)[强制]命名规范:所有命名(类名.属性名.变量名.常量名.属性名)必须以字母开头(a-z.A-Z),不能以特殊字符(_.$)开头. 1.[强制]类名命名规则:大驼 ...
- Mysql join算法深入浅出
导语 联表查询在日常的数据库设计中非常的常见,但是联表查询可能会带来性能问题,为了调优.避免设计出有性能问题的SQL,在explain命令中,会显示用的是哪个join算法,学习一下join过程是非常有 ...
- android点滴-1
一.关于TSpeedButtons 1.对于TspeedButtons,需要选择适当的StyleLookUp值后,才能在ObjectInspector中出现TintColor属性,根据自己需要进行修改 ...
- rabbitmq的高级部分
producer的XML配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&quo ...
- 【FAQ】HarmonyOS SDK 闭源开放能力 — IAP Kit(6)
1.问题描述: 支付场景,表现是在沙盒情况下所有商品都可以正常跑通,但是在非沙盒情况下,线上购买年包1800大额支付华为的 iap.createPurchas 在输完密码就会报 1001860001 ...
- eolinker解决两个变量合并成一个变量提供其他接口使用的方法
特别注意:需要使用全局变量或者预处理前务必阅读本链接https://www.cnblogs.com/becks/p/13713278.html 场景描述:提交订单的接口请求中,有一参数是由商品价格+运 ...
- Solon AI MCP Server 入门:Helloworld (支持 java8 到 java24。国产解决方案)
目前网上能看到的 MCP Server 基本上都是基于 Python 或者 nodejs ,虽然也有 Java 版本的 MCP SDK,但是鲜有基于 Java 开发的. 作为Java 开发中的国产顶级 ...
- tomcat-shell脚本
自动部署项目脚本参考: #!/bin/bash#自动部署脚本source /etc/profileexport LANG=zh_CN.UTF-8#tomcat路径tomcatBinPath=/usr/ ...
- GitLab——重置(reset)和还原(revert)
Git 命令 reset 和 revert 的区别 - 知乎 (zhihu.com) 总结: git reset --hard 9201d9b19dbf5b4ceaf90f92fd4e4019b685 ...
- 使用Linux筛选文本-日志分析
用于简单的文本筛选和日志分析还是很方便的. 我这里用的kali **目的:**筛选出test文件中 状态码为500的url 命令: cat test |grep '500' >test1 或 g ...