首页 > 其他分享 >暑假模拟赛二 解题报告

暑假模拟赛二 解题报告

时间:2023-09-03 09:03:15浏览次数:39  
标签:std p2 p1 long 解题 二叉树 暑假 赛二 define

唐山一中模拟赛一 解题报告

\[\Large 110 \ pts, \text{No.} 1. \]

打这场比赛的时候分心很多,基本上就是 T1 一眼了一下然后实现,然后就开始死摆。一会儿摸鱼一会儿躺着,最后的将近 3 个小时都在摆的过程中偶尔推一下 T2。但显然 T2 打出了正解,但是由于一步小小的错误,加之结论出现了一点偏差,最后喜提 10 分。由于有 T1 的加持,吃相不算难看。感觉哪怕是在打暴力的角度上来讲,也是有很大提升的。比赛态度应该端正。

T1 湮灭反应 - \(100 \ pts\)

主要的分数来源。

看见连续段自然而然地想到两个方面:线段树前缀和。笔者比较熟悉线段树,所以最先考虑了这个方向,发现没有办法查询绝对值最小区间和。所以果断调转矛头,考虑前缀和。不难发现,如果将前缀和排序,和的绝对值最小的一段左右两个端点的前缀和在排序后的数列里必然 连续

因为在打 At 的时候被类似的恶心到过,很自然地就想到了这里,后面就非常简单了。std::multiset 一波存点遍历即可。当然其实本题直接 std::sort 也是可以的。

[ABC308G] Minimum Xor Pair Query

题意:带修,求最小异或对。

解法:注意到最小异或对的两个数在排序后的数列里必然连续,直接扔进一个 std::set 处理即可。

// Author: MichaelWong
// Code: C++14(GCC 9)
// Date: 2023/8/25
// File: annihilation.cpp
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define pii std::pair<ll,ll>
#define fsp(x) std::fixed<<std::setprecision(x)
#define forE(u) for(int p=head[u],v=to[p];p;p=next[p],v=to[p])
const int N=1e5+5;
std::multiset<pii> s;
ll n,a[N],ps[N],ans=0x7fffffffffffffff,len=-1;
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n; s.insert({0,0});
	for(int i=1;i<=n;++i) std::cin>>a[i],ps[i]=ps[i-1]+a[i],s.insert({ps[i],i});
	for(auto p1=s.begin(),p2=++s.begin();p2!=s.end();++p1,++p2) {
		if(ans) {
			if(p2->first-p1->first<ans) ans=p2->first-p1->first,len=std::abs(p2->second-p1->second);
			else if(p2->first-p1->first==ans) len=std::max(len,std::abs(p2->second-p1->second));
		}
		if(p2->first==p1->first) {
			while(p2->first==p1->first) ++p2; --p2;
			len=ans?std::abs(p2->second-p1->second):std::max(len,std::abs(p2->second-p1->second));
			ans=0,p1=p2,--p1;
		}
	}
	if(!n) std::cout<<0<<' '<<0<<'\n';
	else std::cout<<ans<<'\n'<<len<<'\n';
	return 0;
}
// The code was submitted on Luogu for the contest on 8.25.
// Version: 1.
// If I filled in nothing on the statement,
// it means I'm in a contest and I have no time to do this job.

T2 树的计数 - \(10 \ pts\)

这道题是真的冤。本来是想出了正解,但是结论有一处反了,但这样也有 \(60 \ pts\)。但是我为了加速加上减少一点迭代深度,直接上了一个人类智慧表:

std::string sheet[5][20]={
	{},
	{"","X"},
	{"","X(X)","(X)X"},
	{"","X(X(X))","X((X)X)","(X)X(X)","(X(X))X","(((X)X)X)"},
	{"X(X(X(X)))","X(X((X)X))","X((X)X(X))","X((X(X))X)","X(((X)X)X)","(X)X(X(X))","(X)X((X)X)","(X(X))X(X)","((X)X)X(X)","(X(X(X)))X","(X((X)X))X","((X)X(X))X","((X(X))X)X","(((X)X)X)X"}
};

你发现错误了吗?

对!sheet[4] 的第一个忘了加空 ""!直接 \(60 \to 10\)。被评年度最 shaber 错误。

如果你打表打错了,那你还不如换一个逻辑性暴力。 —— Michael Wong

好的,接下来我们说正解。

来看一下每一个结点数的二叉树占的编号数量,

\[1,1,2,5,14,\dots \]

这样的数列显然必须有规律,否则这道题会变得非常难做。(对于笔者来说则是直接不可做。)所以我们找一下规律,发现这个就是 卡特兰数

\[\textbf{卡特兰数通项公式:} C_0 = 1, C_i = \sum_{j=0}^{i-1} C_j \cdot C_{i-j-1}. \]

这样做有正确性吗?我们发现是有的。

  1. 卡特兰数的实际意义是 合法括号序列 的数量。我们可以把 二叉树形态括号序列 进行一一对应的 映射,发现是可以做到的。比如 左儿子在括号外,右儿子在括号内,\(7\) 号二叉树就能表示为 ()(())。每一棵二叉树都能这样表示,对应到一个 唯一且合法的括号序列。这证明了他确实是卡特兰数。
  2. 我们发现,卡特兰数的通项公式也可以用二叉数解释:\(C_i\) 是一棵有 \(i\) 个结点的二叉树,\(C_j\) 表示左子树有 \(j\) 个点,\(C_{i-j-1}\) 表示右子树有 \(i-j-1\) 个点。

所以,我们就可以通过卡特兰数来解这道题了。我们先通过计算卡特兰数列前缀和,得到这个编号对应的二叉树的结点数,然后我们实现一个函数 \(\operatorname{solve} (num,ord)\),表示输出有 \(num\) 个结点的二叉树第 \(ord\) 个方案数。

我们考虑这个编号有什么意义。编号是按照左子树结点数 从小到大 记录的,这显然就是卡特兰数通项公式里面 \(j\) 的遍历顺序!所以我们模拟求解卡特兰数第 \(num\) 位的过程,如果有一个 \(k\) 使得 \(\sum_{j=0}^{k-1} C_j \cdot C_{num-j-1} < ord\) 而 \(\sum_{j=0}^{k} C_j \cdot C_{num-j-1} \geq ord\),那么我们可以断定左子树的大小是 \(k\)。那么,如何求解左子树是 \(k\) 点二叉树的第几个方案呢?

我们考虑 \(C_j \cdot C_{num-j-1}\) 的实际意义,这其实是在把 \(j\) 点二叉树的方案与 \(num-j-1\) 点二叉树的方案进行 一一搭配。在排序上,我们要让左子树编号小的二叉树编号更小,所以其实这形成了一个 横轴是 \(C_{num-j-1}\),纵轴是 \(C_j\) 的方案矩阵。方案数在这个矩阵上 从左至右,从上到下 排列。我们只需要求出这个方案数在矩阵上的坐标,就可以知道他的左右子树编号了。(笔者当时就是横纵轴弄反了……)

// Author: MichaelWong
// Code: C++14(GCC 9)
// Date: 2023/8/25
// File: treecnt.cpp
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define pii std::pair<int,int>
#define fsp(x) std::fixed<<std::setprecision(x)
#define forE(u) for(int p=head[u],v=to[p];p;p=next[p],v=to[p])
const int N=5005;
int n,tot,C[N];
std::string sheet[5][20]={
	{},
	{"","X"},
	{"","X(X)","(X)X"},
	{"","X(X(X))","X((X)X)","(X)X(X)","(X(X))X","(((X)X)X)"},
	{"","X(X(X(X)))","X(X((X)X))","X((X)X(X))","X((X(X))X)","X(((X)X)X)","(X)X(X(X))","(X)X((X)X)","(X(X))X(X)","((X)X)X(X)","(X(X(X)))X","(X((X)X))X","((X)X(X))X","((X(X))X)X","(((X)X)X)X"}
};
void analyze(int num,int ord) {
	if(num<=4) return std::cout<<sheet[num][ord],void();
	for(int i=0;i<num;++i) {
		if(ord<=C[i]*C[num-i-1]) {
			if(i) std::cout<<"(",analyze(i,(ord-1)/C[num-i-1]+1),std::cout<<")";
			std::cout<<"X";
			if(num-i-1) std::cout<<"(",analyze(num-i-1,(ord-1)%C[num-i-1]+1),std::cout<<")";
			break;
		}
		ord-=C[i]*C[num-i-1];
	}
}
signed main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n; C[0]=1;
	while(n) {
		++tot;
		for(int i=0;i<tot;++i) C[tot]+=C[i]*C[tot-i-1];
		if(n<=C[tot]) break;
		n-=C[tot];
	}
	analyze(tot,n);
	return 0;
}
// The code was submitted on Luogu for the contest on 8.25.
// Version: 1.
// If I filled in nothing on the statement,
// it means I'm in a contest and I have no time to do this job.

T3 我没有说谎 - \(0 \ pts\)

非常不应该。这证明我又不会 二进制枚举 了……我已经连续 \(3\) 个二进制枚举暴力没有写了。

\(20 \ pts\) 做法:二进制枚举谁在说谎,判定结果即可。

难点就难在判定上。将 “\(a\) 个人分数比我高,\(b\) 个人分数比我低 ” 转化成 “\([b+1,n-a]\) 这段区间分数相同 ”。判定区间是否有交即可。

由此,我们想到可以施展 DP。\(dp_i\) 表示 \([1,i]\) 最多选取几个不交区间。枚举不要全扫一遍,用一个 std::vector 来记录有多少个区间右端点为当前点,枚举这些区间的左端点即可。

真没想到是 DP……我觉得还是应该想明白判定方法。转移到计算不交区间,赢面就很大了。

// Author: MichaelWong
// Code: C++14(GCC 9)
// Date: 2023/8/26
// File: nolying.cpp
// Human Intelligence!
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define pii std::pair<int,int>
#define fsp(x) std::fixed<<std::setprecision(x)
#define forE(u) for(int p=head[u],v=to[p];p;p=next[p],v=to[p])
const int N=1e5+5;
int n,dp[N];
std::vector<int> lrec[N];
std::map<pii,int> cnt;
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n;
	for(int i=1,a,b;i<=n;++i) {
		std::cin>>a>>b;
		int r=n-a,l=b+1;
		if(l>r) continue;
		lrec[r].push_back(l);
		cnt[{l,r}]<r-l+1&&(++cnt[{l,r}]);
	}
	for(int r=1;r<=n;++r) {
		dp[r]=dp[r-1];
		for(int l:lrec[r]) dp[r]=std::max(dp[r],dp[l-1]+cnt[{l,r}]);
	}
	std::cout<<n-dp[n]<<'\n';
	return 0;
}
// The code was submitted on Luogu.
// Version: 1.
// If I filled in nothing on the statement,
// it means I'm in a contest and I have no time to do this job.

T4 美食家 - \(0 \ pts\)

……

感觉最多想到链表,\(40 \ pts\) 做法。他的这个双指针笔者真的是看了半天才明白,而且他的二分,是 BIT 上二分

// Author: MichaelWong
// Code: C++14(GCC 9)
// Date: 2023/8/26
// File: foodie.cpp
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define pii std::pair<int,int>
#define fsp(x) std::fixed<<std::setprecision(x)
#define forE(u) for(int p=head[u],v=to[p];p;p=next[p],v=to[p])
const int N=1e5+5;
int n,m,x,w[N],id[N],t[N],sum[N],BLN;
inline int lowbit(int x) { return x&-x; }
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n>>m>>x; ++x,BLN=1;
	while(BLN<<1<=n) BLN<<=1;
	for(int i=1;i<=n;++i) std::cin>>w[i];
	for(int i=1,a;i<=n;++i) {
		std::cin>>a; id[i]=i;
		if(w[i]<x) t[i]=x-w[i],t[i]=t[i]/a+(bool)(t[i]%a);
		if(t[i]) for(int j=i;j<=n;j+=lowbit(j)) ++sum[j];
	}
	std::sort(id+1,id+n+1,[](int a,int b){ return t[a]==t[b]?a<b:t[a]<t[b]; });
	int rnd=1,pos=0,ans=0,ptr=1,tmp,las;
	for(;ptr<=n;++ptr) if(t[id[ptr]]) break;
	for(;ptr<=n;++ptr) {
		while(rnd<=m) {
			tmp=n-ptr+1;
			for(int i=pos;i;i-=lowbit(i)) tmp-=sum[i];
			if(ans+tmp<t[id[ptr]]) ans+=tmp,++rnd,pos=0;
			else break;
		}
		if(rnd>m) break;
		las=t[id[ptr]]-ans;
		for(int i=pos;i;i-=lowbit(i)) las+=sum[i];
		pos=tmp=0;
		for(int lb=BLN;lb;lb>>=1) {
			pos+=lb;
			if(pos>=n||tmp+sum[pos]>=las) pos-=lb;
			else tmp+=sum[pos];
		}
		++pos;
		ans=t[id[ptr]];
		for(;t[id[ptr]]==t[id[ptr+1]];++ptr) for(int i=id[ptr];i<=n;i+=lowbit(i)) --sum[i];
		for(int i=id[ptr];i<=n;i+=lowbit(i)) --sum[i];
	}
	std::cout<<ans<<'\n';
}
// The code was submitted on Luogu.
// Version: 1.
// If I filled in nothing on the statement,
// it means I'm in a contest and I have no time to do this job.

总结

总的来讲,暴力的分还是打的太少了,也看到了其实对 DP 和 双指针模拟 的认识还是有局限性的。关键的点在于把问题转化,变成一个比较格式化的问题。(虽然感觉 T4 代码也不怎么是人写的。)

标签:std,p2,p1,long,解题,二叉树,暑假,赛二,define
From: https://www.cnblogs.com/michaelwong007/p/Summer_Contest_2.html

相关文章

  • 暑假第二周周记
    本周休息在家,什么都没做,每天都在家里休息,享受假期时光,没有学习。周一,在家休息,早上起床吃饭后就玩游戏,玩游戏玩的无聊了就去看小说,中午吃完饭睡个午觉起床继续打游戏,打游戏无聊了继续看小说。吃完晚饭之后会帮家里洗碗,晚上打会儿游戏,十点准时洗澡睡觉。周二,在家休息,......
  • 暑假生活第七周
    本周我在家继续学习Python编程,并且挑战了一些更难的内容。以下是我的学习总结:学习时间:我每天平均投入8个小时学习Python编程,总共学习了40个小时。学习内容:我主要集中在学习Python编程的高级概念和技术。具体包括以下几个方面:面向对象编程(OOP):我深入学习了Python中的类、对象......
  • LeetCode952三部曲之一:解题思路和初级解法(137ms,超39%)
    欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos题目描述难度:困难编程语言:Java给定一个由不同正整数的组成的非空数组nums,考虑下面的图:有nums.length个节点,按从nums[0]到nums[nums.length-1]标记;只有当......
  • 《CF1863》 解题报告
    题面传送器首先有一个\(naive\)的做法。直接\(O(n^3)\)暴力判断。考虑寻找突破口。假如给了你一个序列,异或值为\(S\),那么实际上假如中间有一个断点\(mid\),那么我们最终决定保留哪一段,实际上是看\(S\)的最高位\(1\)的位置来比较的,所以我们只需要管最高位的\(1\)......
  • 暑假第一周周记
    放假第一天回到了武汉,当天中午坐上回武汉的高铁,然后晚上落地整理好相关行李之后出门吃饭,吃的火锅,双人套餐,分量较少且味道不好,以后不去吃了。吃完回去睡觉,没有进行web方面的学习。放假第二天一觉睡到了中午后陪姐姐过生日在外面吃饭,中午吃完饭后在武汉大悦城商场进行了购......
  • 基环树问题 解题报告
    luoguP5022旅行题意对于60%的数据,给一棵树,求一条字典序最小的Hamilton路径;对于40%的数据,给一颗基环树,求一条字典序最小的Hamilton路径。分析前向星存图,对于每个点的出边排序,从1开始dfs一遍即可过60%数据;对于基环树,由于Hamilton路径在树上必然有一条边不经过,而这条边必然......
  • 《AT_arc106_d》 解题报告
    来一道简单数论。求\(\sum\limits_{l=1}^{n-1}\sum\limits_{r=l+1}^{n}(a_l+a_r)^x\),其中\(1\lex\lek\)\(n\le2e5,k\le300\)显然是一个\(O(nk)\)的做法我们来推式子\[\begin{aligned}\sum\limits_{l=1}^{n-1}\sum\limits_{r=l+1}^{n}(a_l+a_r)^x&=\sum\li......
  • The 2021 ICPC Asia Shenyang Regional Contest 解题报告
    The2021ICPCAsiaShenyangRegionalContestsolo七题罚时738打到金尾了,但是这个G和I也应该是自己能做出来的。G找了若干性质确实转化到最后一步了。但本应该搞出的dp没有想到。G和M感觉都有点降智。而I则是被复数吓到了。有点菜。B:拆位,扩展域并查集。E:签到。F......
  • Atcoder Beginner Contest 317 解题报告
    AtcoderBeginnerContest317ABC316咋没了。暂时A~E。HintsD$\quad$可以算出每次选举需要的改票数。然后变成了一个经典问题。E$\quad$有点naive。不用担心暴力扫T掉,时间复杂度是真的。F$\quad$F1$\qquadn$这么大一维都枚举不了……诶,$a_i$只有$10$?$\qua......
  • 解题报告 8月
    0717T1patkice题意不是很久之前,在一个遥远的大陆上,住着三只橡皮鸭。在一个炎热的夏日,躺在沙滩上的鸭子们决定旅行到一座相邻的小岛(用一把老旧的黑色雨伞)。鸭子们是经验丰富的海洋探险家,在旅行之前,他们会检查当前海洋的海图。在海图上,鸭子们目前居住的岛屿被字符o标记,鸭子们......