NOIP考前练习基础题想法记录

作为一名来自弱省的蒟蒻,面对即将到来的NOIP,唯一能做的就是祈祷(划去),就是刷题,本蒟蒻的愿望就是争取T1T2不丢分,T3T4就听天由命看人品了(rp++)。刷了一些前几年的题目,感觉前两道题的一些其实是存在一些解题想法的,本蒟蒻就想记录一下这段时间自己的心得。

  1. https://www.luogu.com.cn/problem/CF23B
    这道题一眼瞅过去我其实是有点蒙的(甚至题目都有点没读懂),读了几次后明白了就是从度数为0的点开始删去这个点,同时将连着这个点的边上的另一个点的度数减一,并且下次删去度数为1再减连接的度数,并这么延续到n-1为止。这么听起来还是很抽象,那么我们便从少开始列举,首先我选择从n = 4开始分析,上图。!(请原谅我粗糙的画工)
    那么我们想想怎样才能让尽可能多的人不被点到,首先我们如果将所有的点都连在一起。

    (能用上cs了就是爽)
    此时很明显所有人的朋友数都是3,全部会被点走,那么我们如果将任意一个关系去掉的话,那么这两个人就会因为彼此不是朋友了,朋友数目减少了一个,而其余的2个人仍然是原先的朋友数目,这里面我们假设将1和4之间的朋友关系去掉。

    那么我们会发现1和4的朋友数为2,2和3的朋友数仍然为3,那么当去掉朋友数为2的人时,1和4便离开,2和3因为和1、4都是朋友,朋友数变成了1,所以此时的最大值即为2。我们便会发现,如果将所有的人排成一排,两边的两个人都不跟对方相连,而中间的其他人与非自己的所有人相连,那么此时只会让两边的两个人离开,而中间剩下的人数既为剩余的最大人数。在这里我画一个n = 6时的情景。
    (强迫症状患者表示很淦)
    我们发现这个结论是成立的,并且可以推广到之后的人数。但是需要注意的是,如果人数为1或2时,此时一定不会剩下任何一个人,那么我们便可以得出。

\[ ans=\left\{ \begin{aligned} 0, 0 \leq n \leqslant 2 \\ n - 2, n \geq 2 \end{aligned} \right. \]

那么接下来,皆大欢喜的上代码。

Code(点击打开)

#include
using namespace std;
int n,t;
int main(){
	cin >> t;
	while(t--){
		cin >> n;
		if(n == 1) cout << 0 << endl;
		else if(n == 2) cout << 0 << endl;
		else cout << n - 2 << endl;
	}
	return 0;
}
继续下一题~ 2.[https://www.luogu.com.cn/problem/CF437C](The Child and Toy) 这道题的意图就很明显了,由于删除的是点权,并且是要求最小值,那么我最开始想到的便是贪心,再一想,删点实际上可以从删边的角度来思考,我们一共需要删除的是n - 1条边,那么便将每条边都遍历一遍,代价增加这条边所连接的两个点中的点权小的点权,非常简单。
Code

#include
#include
using namespace std;
int n,m,a[10010];
long long ans;
int main(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++) cin >> a[i];
	for(int i = 1; i <= m; i++){
		int x,y; cin >> x >> y;
		ans += min(a[x], a[y]);
	}
	cout << ans << endl;
	return 0;
}
3.[https://www.luogu.com.cn/problem/CF61D](Eternal Victory) 嗯,我们看一下这道题的题面,提到n个城市,但是只有n - 1条边,并且两个点之间只有一条**双向**通道,那么很明显,这道题应该是树上的问题。那么根据题意,当在树上的一点的时候,可以选择他所连接的一条路走,然后再返回那么选择的边便会游历两次,但是会有一条路径是只走一次的,想要求出最短路径,就应该让树中最长的一条链直走一次,其他的边走两次便可以得出正确答案,所以便是树的总路径乘以2再减去最长的一条链的长度便可。
Code

#include
#include
define ll long long 
using namespace std;
int cnt,head[100010];
ll ans = 0,len = -1,n;
struct edge{
	int to,next,dis;
}edge[100010 << 1];
void add(int x, int y, int t){
	edge[++cnt].to = y;
	edge[cnt].next = head[x];
	edge[cnt].dis = t;
	head[x] = cnt;
}
void dfs(int x, int y, ll now){
	len=max(len, now);
	for(int i = head[x]; i; i = edge[i].next){
		int v = edge[i].to,d = edge[i].dis;
		if(v == y) continue;
		dfs(v, x, now + d);
	}
	return ;
}
int main(){
	cin >> n;
	for(int i = 1; i <= n - 1; i++){
		int x,y,w; cin >> x >> y >> w;
		add(x, y, w); add(y, x, w);
		ans += w;
	}
	ans *= 2;
	dfs(1, 0, 0);
	cout << ans - len << endl;
	return 0;
}
posted @ 2020-10-24 15:34  summitsoul  阅读(79)  评论(0编辑  收藏  举报