暑假の刷题1

7.23开始

看字数分文章吧10000字一篇不然太难找了了;

P5021 NOIP2018

这道题刚看见的时候读不懂这题是啥意思,多读了几遍才知道,然后想了想骗分只能骗到55而且也挺好写的,后来就不知道怎么做了,50的范围我都想不出什么狗屁方法,想了一个小时之后,然后去看题解了,发现这题其实很简单,只是我刷题太少人比较笨想不出来;
二分答案基本上是可以肯定了的,好像最大值最小和最小值最大基本没有逃出过二分的范围,之前还怀疑会不会二分只是一种解答而已,题目中给了个限制叫做选的赛道的条数,这个条件很重要就是拿来check的基础,刚开始做的时候我甚至想到这点都有点困难,然后分析一下用那种二分;
树上处理的问题被标签迷惑了,一直在想lca能怎么做,后来发现这就是多此一举根本用不到lca;

阅读了几个阳间题解,觉得下面几句话写得很好:

3.题目中的这句话“一条赛道是一组互不相同的道路 ,满足可以从某个路口出发,依次经过这些道路(每条道路经过一次,不允许调头)到达另一个路口。”给了我们第一个提示:对于一个节点,与和它相连的子节点之间,最多只能选取两条边组成赛道。因为如果边数超过2,很明显是无法在不掉头的情况下一次性走完这条赛道的。(比如下图)
4.题目中的这句话“要求每条道路至多被一条赛道经过”给了我们第二个提示:对于一个节点,与和它相连的子节点未被选取的边中,最多只有一条可以对之后的答案做出贡献,我们只要把这条边的的长度向上传递就好。因为该节点与它父节点之间只有一条边,只可以建立一条赛道。(比如下图)

这个解读狠不戳;
在这里插入图片描述
题目的数据范围表格给了启发,其实就是菊花图和二叉树或链的结合一下,就是说一部分是直接可以在当前节点上直接了解,另一部分要一直延伸上去;
还有一张图画得很好;

在这里插入图片描述
很形象地解构出来了,以后做题的时候可以参考一下做法,前面想的时候就光想了。
这题目的精髓是其结构这棵树的方式比较奇特;既有挂在一个节点上的链也有伸上去的很有启发意义;
做这题时候的缺陷就是没有很确定的想到二分时候怎么去check,如果想到的话那么会效率更高的想到后面的步骤;
可以和树网的核类比一下都是树上问题;
反思一下,这两道题看了题解都很简单,之所以做不出来就是check函数的设计出了问题,其实多仔细想想就能解决的东西;
代码
我们可以证明这个代码的正确性;
证明略;
学学这个代码的细节处理还是很妙的;
对我这种想不来更写不来的菜逼来说;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define debug puts("OK")
const int INF = 0x3f3f3f3f,N = 5e4+10, M = 1e5+100;
int h[N],w[M],ne[M],e[M],idx;
void add(int a,int b,int c){
   
	e[idx] = b, w[idx] = c,ne[idx] = h[a], h[a] = idx++;
}
int n,m,res,tail;
int que[M],dp[M],tag[M];
void dfs(int u,int fa,int x){
   
	for(int i = h[u];~i;i = ne[i])
		if(e[i] != fa)
			dfs(e[i],u,x);
	tail = 0;
	for(int i= h[u];~i;i=ne[i]){
   
		if(e[i]!= fa)
			que[++tail] = dp[e[i]] + w[i];
	//		printf("%d\n",que[tail]);
	}
	sort(que+1,que+tail+1);
	for(int i = tail; i>=1&&que[i] >= x;i--){
   
		tail -- ,res -- ;
	}
	for(int i = 1;i<=tail;i++){
   
		if(tag[i]!= u){
   
			int l = i+1, r = tail , pst = tail + 1;
			while(l<=r){
   
				int mid = (l+r) >> 1;
				if(que[i] + que[mid] >= x){
   
					pst = mid , r = mid -1;
				}else l = mid + 1;
			}
			while(tag[pst] == u &&pst<=tail){
   
				pst ++ ;
			}
			if(pst <= tail)
				tag[i] = tag [pst] = u, res--;
		}
	}
	dp[u] = 0;
	for(int i = tail;i >= 1;i--){
   
		if(tag[i] != u){
   
			dp[u] = que[i];
			break;
		}
	}
	return;
}bool check(int x){
   
	res = m;
	memset(tag,0,sizeof tag)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值