首页 > 其他分享 >「模拟赛」暑期集训CSP提高模拟4(7.21)

「模拟赛」暑期集训CSP提高模拟4(7.21)

时间:2024-07-24 21:42:23浏览次数:14  
标签:int sum 7.21 节点 枚举 Black White CSP 模拟

很祭的一次比赛,啥也不会。


题目列表:

A.White and Black

B.White and White

C.Black and Black

D.Black and White


A.White and Black

\(0 pts\)

题意:

给你一颗树,树根为 1 ,初始所有点都是白色,每次询问给出一个点集,若把点集中的点全部染成黑色,问至少需要翻转多少个节点才能使整棵树全为白点。无解输出 -1。询问之间相互独立

翻转的定义为:将节点 u 及其子树中所有节点颜色翻转。

赛时分析:

赛时不会,想到了时间复杂度 \(O(n * m)\) 的树上 DP,以为是正解(时间复杂度想假了 \(n\) 按 \(log\large{n}\) 算的),打完才发现时间复杂度不对,之后就只想着怎么在这个基础上优化了,一直在思考把 \(n\) 变成 \(log \large{n}\),本地跑大样例从 \(36s\) 卡常优化到了 \(1.9s\),但时间复杂度没变,大概调了一个多小时急了,直接交了,想着能拿个 \(30pts\),特殊性质也没想,结果捆绑测试实际得分 \(0pts\)。

以后模拟赛不能一个思路耗死,不能太高估自己,像这个题,性质也没那么难想,但自己完全就是没想着会有什么性质,一直在优化自己的 dp,看来以后要及时否定自己多换思路。

正解:

注:下文“染黑某个点”等说法皆指染黑以该点为根的子树。

思想:

我们枚举每个被染黑的点,设为 \(u\),由于 \(u\) 已经被染黑了,所以我们肯定要把它染回来,如果 \(u\) 的父亲节点 \(fa\) 也为黑点,那么我们在进行将 \(fa\) 染为白点的操作后,自然也一起把 \(u\) 染白了;
再枚举 \(u\) 的子节点 \(son\),如果 \(son\) 为白色,那么在把 \(u\) 染回来的操作中,\(son\) 点便被染黑了,所以还需要再染一次 \(son\) 点。

计算答案 \(ans\) 时,那么我们只需遍历每个被染黑的点,若它的父亲节点不是黑点,则需要染一次,\(ans++\),再遍历它的子节点,若子节点是白点,则还需染子节点一次,\(ans++\)。

这便是我们的思想,不过较容易发现,时间复杂度为 \(O(Q n m)\),(\(Q\) 为询问的次数),会 \(T\) 掉。

暴力代码
#include<bits/stdc++.h>
#define mp make_pair
#define ll long long
using namespace std;

const int N = 2e5 + 10;

int n, Q, m, in[N];
int color[N], fa[N];
map<pair<int, int>, int>son;

signed main(){
	// freopen("in.in", "r", stdin); freopen("a.out", "w", stdout);

	scanf("%d%d", &n, &Q);
	for(int i=2; i<=n; i++){
		scanf("%d", &fa[i]);
		son[mp(fa[i], ++son[mp(fa[i], 0)])] = i;
	}


	while(Q--)
	{
		scanf("%d", &m);
		for(register int i=1; i<=m; i++){
			scanf("%d", &in[i]);
			color[in[i]] = 1;
		}

		int ans = 0;
		for(int i=1; i<=m; i++){
			if(!color[fa[in[i]]]) ans++;
			for(int j=1; j<=son[mp(in[i], 0)]; j++){
				if(!color[son[mp(in[i], j)]]) ans++;
			}
		}

		printf("%d\n", ans);

		for(register int i=1; i<=m; i++) color[in[i]] = 0;
	}


	return 0;
}

考虑优化:

时间复杂度多的那个 \(n\) 是枚举每个黑点的儿子节点导致的,所以我们考虑能不能不枚举每个儿子,我们可以预处理出来的时每个节点儿子的数量,这些数量是不会变化的,“别管黑儿子还是白儿子都是儿子”。

所以在枚举每个黑点的时候,我们可以直接加上该点儿子的数量。但这样就多加了黑儿子啊,按照我们的思路只加白儿子,但别忘了,黑儿子是被染黑的点,所以我们枚举所有黑点时总会枚举到这些多加的黑儿子,那么我们枚举时,判断枚举点的父亲节点是否为黑,若为父亲节点为白,则加一次操作,否则把减去一次操作,这样就把多加的黑儿子减去了。

成功优化到 \(O(Q n)\) 了!

最终代码:

#include<bits/stdc++.h>
#define mp make_pair
#define ll long long
using namespace std;

const int N = 2e5 + 10;

int n, Q, m, in[N];
int color[N], fa[N], son[N];

signed main(){
	// freopen("in.in", "r", stdin); freopen("a.out", "w", stdout);

	scanf("%d%d", &n, &Q);
	for(int i=2; i<=n; i++){
		scanf("%d", &fa[i]);
		son[fa[i]]++;
	}


	while(Q--)
	{
		scanf("%d", &m);
		for(register int i=1; i<=m; i++){
			scanf("%d", &in[i]);
			color[in[i]] = 1;
		}

		int ans = 0;
		for(int i=1; i<=m; i++){
			if(!color[fa[in[i]]]) ans++;
			else ans--;
			ans += son[in[i]];
		}

		printf("%d\n", ans);

		for(register int i=1; i<=m; i++) color[in[i]] = 0;
	}


	return 0;
}



White and White

题意:

怀特有一个长度为 \(n\) 的正整数数列 \(A\) ,现在他在研究一种数列划分方法。

划分是将这个这个数列分成 \(k\) 个非空连续段,每段的价值为这段所有数的总和对给定的怀特数 \(p\) 取模后的值。

使得这 \(k\) 段的价值总和最小的划分方法就被成为怀特划分法。



Black and Black

题意:

布莱克现在有一个长度为 \(n\) 的序列 \(A\),但是由于他的 dp 不好,所以 \(A\) 序列只会出现 1,−1 这两个数,虽然他也不知道这有什么用,但是需要 dp 的时候这样会变简单吧

为了给以后的 dp 做更多准备,现在他想求出一个布莱克序列 \(B\) ,布莱克序列 \(B\) 满足以下几个限制

  • 序列长同样为 \(n\)
  • \(∣Bi​∣≤2×10^{12};\)
  • 序列严格递增,即 \(∀1≤i<n,Bi​<Bi+1​\);
  • \(∑_{i=1}^nAiBi=0\)。

给定一个序列,求是否是布莱克序列。

赛时分析:

其实赛时基本想到正解了,但由于在 T2 卡的时间过长,所以 T3 根本没敢仔细想即使是已经出现正解的思路,只是在最后半小时打完了 T3 和 T4 的暴力。

正解:

构造。我们先将 \(b_i\) 赋值为 \(i\),并求 \(∑_{i=1}^n b_i * a_i\) 的和记为 \(sum\),求一个 \(s\) 数组为 \(a\) 数组的前缀。

  • 若 \(sum=0\) 直接输出即可

  • \(sum>0\) 时

    • 从头到尾扫一遍 \(s\),找到第一个 \(s_i>0\) 的情况,(由于是第一个,那么一定有找到的 \(s_i=1\) )那么说明我们可以把 \(b_j(j\in[1, i])\) 都减去 \(sum\),等价于整个 \(b\) 数组的总和减去了 \(sum\),那么总和就为 0 了;
    • 若未扫到,那再从尾到头再扫一遍,找到第一个 \(s_n - s_i<0\) 的情况(同理 \(s_n-s_i=-1\)),这样我们把 \(b_j (j\in[i, n])\) 全都加上 \(sum\) 即可;
    • 还是找不到的话,那么说明无解,输出 -1。
  • \(sum<0\) 时,与上述相反操作即可。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 2e5 + 10;

int n, a[N]; ll b[N], sum;
ll s[N];

inline void print(){
	puts("Yes");
	for(int i=1; i<=n; i++) printf("%lld ", b[i]);
}

int main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);

	scanf("%d", &n);
	for(int i=1; i<=n; i++){
		scanf("%d", &a[i]);
		s[i] = s[i-1] + a[i];
		b[i] = i; sum += i * a[i];
	}

	if(sum == 0) print();
	else if(sum < 0){
		for(int i=n; i>=1; i--){
			if(s[n] - s[i-1] > 0){
				for(int j=i; j<=n; j++){
					b[j] += -sum;
				}
				print();
				return 0;
			}
		}
		for(int i=1; i<=n; i++){
			if(s[i] - s[0] < 0){
				for(int j=1; j<=i; j++){
					b[j] += sum;
				}
				print();return 0;
			}
		}
		puts("No");
	}
	else{
		for(int i=n; i>=1; i--){
			if(s[n] - s[i-1] < 0){
				for(int j=i; j<=n; j++)
					b[j] += sum;
				print();
				return 0;
			}
		}
		for(int i=1; i<=n; i++){
			if(s[i] - s[0] > 0){
				for(int j=1; j<=i; j++)
					b[j] -= sum;
				print();
				return 0;
			}
		}
		puts("No");
	}



	return 0;
}



Black and White

题意:

给你一颗树,边长均为1,初始所有点都是黑色,有以下两种操作:

  • C u 将点 \(u\) 的颜色反转,即黑变白,白变黑;
  • G 询问距离最远的两个黑点的距离;

对于每一个操作 G,输出一个整数,表示最远的两个黑点的距离。

还不会...

标签:int,sum,7.21,节点,枚举,Black,White,CSP,模拟
From: https://www.cnblogs.com/YuenYouth/p/18314661

相关文章

  • 2024.7.22模拟赛5
    模拟赛咕了两天才发现少了一天的题解。T1MakeItIncreasing水。code#include<bits/stdc++.h>usingnamespacestd;constintN=40;#defineLLlonglongintt,n;LLa[N];intmain(){// freopen("in.in","r",stdin);// freopen("out.out",&......
  • 暑假集训CSP提高模拟6
    赛时在\(T2\)浪费时间太多,导致\(T4\)暴力没时间想了,总是想把\(T2\)这种题当大型分讨来做A.花间叔祖[ARC148A]modM观察性质可以发现,答案要么是1,要么是2,把是1的情况找出来剩下的就是2。考虑什么时候是1,如果一个数列模上一个数结果相同,那么他们的差一定是这个模数的整......
  • 暑假集训CSP提高模拟3
    暑假集训CSP提高模拟32/35反向挂了若干分又正向挂了若干T1abc猜想水,随便变形推个柿子糊个快速幂就好了T2简单的排列最优化题考虑只计算每次右移的\(delta\),我们发现一个点只会在到贡献为\(0\)的位置和序列开头会改变对\(delta\)的贡献,直接算就好,\(O(n)\)的T3简单......
  • [题解]P9755 [CSP-S 2023] 种树
    P9755[CSP-S2023]种树迟来的补题本题是让最小化所有树长到指定高度日期的最大值,于是想到二分答案。那么,对于一个给定的期限\(x\),如何判断是否能在这个日期内完成任务呢?首先我们发现前\(n\)天每天都要种树,那么假设我们已经知道了每个地块最晚哪个日期种树,能保证在期限\(x\)......
  • 20240724【省选】模拟
    挂了四分,掉了一名,不过这也说明我的实力就只有这点,根本不够,果然以后还是直接【数据删除】得了。T1其实就是个树剖,每个点维护左右子树的最大深度以及左右子树内的最大答案,然后就…………没了?淦,也是实现问题,应该想到的。然后就是修改边权是改成\(w-a_p\),\(a_i\)是记录下来的\(i......
  • Linux系统安装Cobol语言及IBM大型机模拟软件Hercules
     COBOL(CommonBusiness-OrientedLanguage)起源于50年代中期,是一种面向过程的高级程序设计语言,主要用于商业和数据处理领域。经过不断发展和标准化,已成为国际上应用最广泛的商业编程语言之一,在某red书上还有招聘COBOL程序员去日本的帖子,个人害怕噶腰子所以不推荐。COBOL语言具......
  • SPONGE常用教程:蛋白+配体模拟1
    软件支持SPONGE(SimulationPackagetOwardNextGEnerationmolecularmodelling)是由北京大学高毅勤课题组开发的分子动力学模拟程序。安装教程XPONGE使用python编写的分子动力学模拟前后处理工具。简易安装:pipinstallgit+https://gitee.com/gao_hyp_xyj_admin/xponge.gitDS......
  • YC322A [ 20240724 CQYC NOIP 模拟赛 T3 ] 小 M 的字符串(string)
    题意给定一个\(0/1\)字符串,你需要从中选出尽可能多的不相交的子串使得按顺序字典序单调递增。\(n\le25000\)。Sol先考虑能最多选出多少个不相交的子串,这个是\(\frac{n}{\logn}\),但是这个没用。考察一下子串的长度,由于字典序的限制,最劣的情况下就是一个子串比上一个子串......
  • 『模拟赛』暑假集训CSP提高模拟6
    RankA.花间叔祖签到题,但没切。一眼出思路找最大公因数,过了大样例发现同余的情况也合法,然后开始优化,成功从68pts到了88pts。考虑正解,首先答案不是一就是二。若答案是一,即所有数可在模某数\(x\)意义下同余。不妨将每个数化成\(a_i=k\timesx+d\)的形式,则若存在一个......
  • 【数据结构】队列详解和模拟实现
    大家好,今天我们学习队列,本篇博客主要涉及一般队列,环形队列和双端队列,每个队列应用场景均有所差异,下面我们一起来看看吧。队列(Queue)一,概念队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性入队列:进行插入操作的一端称为队尾(Ta......