CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes)(A-C)

A. Good Pairs

题意:

思路:

观察上面的数学等式,有没有发现只要绝对值开出来不变号,那么一定成立?所以取个最大和最小即可

#include<bits/stdc++.h>

using namespace std;

const int maxn=2e5+100;
struct node{
	int v,idx;	
}a[maxn];
bool cmp1(node a,node b)
{
	return a.v<b.v;
}
int main()
{
	int n,i,j,t;
	cin>>t;
	while(t--)
	{
		cin>>n;
		for(i=0;i<n;i++)
		{
			cin>>a[i].v;
			a[i].idx=i+1;
		}
		sort(a,a+n,cmp1);
		cout<<a[0].idx<<" "<<a[n-1].idx<<endl;
	}
	return 0;
}

B. Subtract Operation

题意:

您将得到一个n整数列表。您可以执行以下操作:从列表中选择一个元素x,从列表中擦除x,并从其余所有元素中减去x的值。因此,在一个操作中,列表的长度精确地减少了1

思路:

拿三个数玩一下就知道了,比如 1 2 3 ,假设把1拿走,剩下就是1 2 ,再拿走一个就是1,有没有发现,一直拿下去,到最后剩下的两个数时,他们的差和变化之前是一样的,也就是说本来最后两个假设是2和3,他们的差就是1,最后虽然是1和2,但是他们的差还是1,所以既然每个数的差是固定的,那么其实我就找数组中有没有任意两个数的差为k就好

#include<bits/stdc++.h>

using namespace std;
map<long long ,long long >mo;
#define int long long 
const int maxn=2e5+1000;
int a[maxn];
signed main()
{
	int n,i,j,t,k;
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		for(i=0;i<n;i++)
		{
			cin>>a[i];
			mo[a[i]+k]++;
		}
		int flag=0;
		for(i=0;i<n;i++)
		{
			if(mo[a[i]]){
				flag=1;
				break;
			}
		}	
		if(flag) cout<<"YES"<<endl;
		else cout<<"No"<<endl;
		mo.clear();
	}
	return 0;
}

C. Make Equal With Mod

题意:

您将得到一个nn个非负整数的数组a1,a2,…。,a2,…...您可以进行以下操作:选择一个整数x≥2x≥2,并将数组的每个数除以xx,即对于所有1≤i≤N Set ai到ai%x时,将该数组的每个数替换为余数。

思路:

整体操作和上面一样,不过是用x%上每个数然后用他们的余数去代替,并且选的x可以任意,但是得大于等于2,首先不难发现,既然x随意选,那么只要大于2的都用它本身去取余直接就为0了,于是乎你从大到小一个数一个数余小去都可以得到0,那么没有出现1的情况都有解,现在思考有1的情况。

①有1的情况下如果有0那么一定是NO,因为无法进行取余操作,他们不会改变了

②有1的情况下1肯定是固定的,想办法把他们都变为1,那么余本身是0,余本身-1就是1了,所以就排序找一下数组里有没有出现过比他小1的即可。

我考场代码比较乱,最后几分钟过的。。哎,之前一直猜结论就芭比Q了,也是灵光一闪过的

#include<bits/stdc++.h>

using namespace std;
const int maxn=2e5+1000;
int a[maxn],c1[maxn];
signed main()
{
	int n,i,j,t,k;
	cin>>t;
	while(t--)
	{
		cin>>n;
		int f1=0,f2=0,d1=0,d2=0,cnt=0,b1=0;
		for(i=0;i<n;i++)
		{
			cin>>a[i];
			if(a[i]==1) d1=1;
			else if(a[i]==0){
				d2=1;
			}
			else 
				c1[cnt++]=a[i];
				if(a[i]==2) b1=1;
			if(a[i]%2==0) f2++;
			else f1++;
		}
		if(d1==0||f1==n||f2==n)
		{
			cout<<"YEs"<<endl;
		}
		else if(d1&&d2)
		{
			cout<<"No"<<endl;
		}
		else  
		{
			int dx=0,flag=0;
			sort(c1,c1+cnt);
			for(i=1;i<cnt;i++)
			{
				if(c1[i]-c1[i-1]==1) flag=1;
			}
			if(flag==0&&b1==0)
			{
				cout<<"YES"<<endl;
			}else 
			{
				cout<<"No"<<endl;
			}
		}
	}
	return 0;
}

表: Contests +--------------+------+ | Column Name | Type | +--------------+------+ | contest_id | int | | gold_medal | int | | silver_medal | int | | bronze_medal | int | +--------------+------+ contest_id 是该表的主键. 该表包含LeetCode竞赛的ID和该场比赛中金牌、银牌、铜牌的用户id。 可以保证,所有连续的比赛都有连续的ID,没有ID被跳过。 Table: Users +-------------+---------+ | Column Name | Type | +-------------+---------+ | user_id | int | | mail | varchar | | name | varchar | +-------------+---------+ user_id 是该表的主键. 该表包含用户信息。 编写 SQL 语句来返回 所有面试候选人 的姓名 name 和邮件 mail 。当用户满足以下两个要求中的 任意一条 ,其成为 面试候选人 : 该用户在 连续三场及更多 比赛中赢得 任意 奖牌。 该用户在 三场及更多不同的 比赛中赢得 金牌(这些比赛可以不是连续的) 可以以 任何顺序 返回结果。 查询结果格式如下例所示。 示例 1: 输入: Contests表: +------------+------------+--------------+--------------+ | contest_id | gold_medal | silver_medal | bronze_medal | +------------+------------+--------------+--------------+ | 190 | 1 | 5 | 2 | | 191 | 2 | 3 | 5 | | 192 | 5 | 2 | 3 | | 193 | 1 | 3 | 5 | | 194 | 4 | 5 | 2 | | 195 | 4 | 2 | 1 | | 196 | 1 | 5 | 2 | +------------+------------+--------------+--------------+ Users表: +---------+--------------------+-------+ | user_id | mail | name | +---------+--------------------+-------+ | 1 | sarah@leetcode.com | Sarah | | 2 | bob@leetcode.com | Bob | | 3 | alice@leetcode.com | Alice | | 4 | hercy@leetcode.com | Hercy | | 5 | quarz@leetcode.com | Quarz | +---------+--------------------+-------+ 输出: +-------+--------------------+ | name | mail | +-------+--------------------+ | Sarah | sarah@leetcode.com | | Bob | bob@leetcode.com | | Alice | alice@leetcode.com | | Quarz | quarz@leetcode.com | +-------+--------------------+ 解释: Sarah 赢得了3块金牌 (190, 193, and 196),所以我们将她列入结果表。 Bob在连续3场竞赛中赢得了奖牌(190, 191, and 192), 所以我们将他列入结果表。 - 注意他在另外的连续3场竞赛中也赢得了奖牌(194, 195, and 196). Alice在连续3场竞赛中赢得了奖牌 (191, 192, and 193), 所以我们将她列入结果表。 Quarz在连续5场竞赛中赢得了奖牌(190, 191, 192, 193, and 194), 所以我们将他列入结果表。
最新发布
10-23
<think>我们需要从Contests表和Users表中查询满足以下任一条件的用户: 条件1:在连续三场及更多比赛中赢得任意奖牌(即在同一用户连续三场比赛中都获得了金牌、银牌或铜牌中的任意一种) 条件2:在三场及更多不同的比赛中赢得金牌(不要求连续) 注意:条件1要求连续三场,条件2不要求连续,只要有三场及以上不同的比赛获得金牌即可。 我们有以下表结构: Contests表:contest_id(比赛ID,连续且无跳过)、gold_medal(金牌用户)、silver_medal(银牌用户)、bronze_medal(铜牌用户) Users表:user_id, name, mail 等 思路: 1. 条件2比较容易:直接按金牌分组,统计每个用户获得金牌的次数,然后筛选次数>=3的用户。 2. 条件1:需要判断一个用户是否在连续三场比赛中都获得了奖牌(任意奖牌)。这里连续三场比赛是指比赛ID连续的三场比赛,且该用户在这三场比赛中都至少获得一枚奖牌(金牌、银牌或铜牌均可)。 对于条件1,我们可以采用以下方法: 首先,将每一场比赛的金牌、银牌、铜牌用户都拆分成一行,这样每个用户在一场比赛中如果获得多个奖牌,也会有多条记录(但同一个用户在同一场比赛只会有一条记录?注意:一个用户在同一场比赛不可能同时获得两个奖牌吗?题目没有说明,但通常一个比赛同一用户只能获得一个奖牌,但为了保险,我们可以去重)。但题目中,一场比赛的金银铜牌都是不同的用户,所以同一场比赛同一个用户不会重复。因此,我们可以用union all将三列合并成一列,然后去重?实际上不需要去重,因为一个用户在一场比赛中只会出现在一个奖牌位置。 但是,注意条件1是连续三场比赛中都获得奖牌,也就是说,该用户必须出现在连续三场比赛的奖牌名单中(任意奖牌)。因此,我们需要为每个用户收集他获得奖牌的所有比赛ID,然后判断是否存在连续的三个比赛ID。 方法: 我们可以先为每个用户列出他获得奖牌的所有比赛ID(去重,因为同一场比赛同一个用户只会有一个奖牌),然后按照比赛ID排序,然后利用窗口函数计算连续条件。 具体步骤: 步骤1:创建一个临时表(或CTE)列出所有获得过奖牌的用户和对应的比赛ID(包括金、银、铜牌)。 prize表:user_id, contest_id 步骤2:对每个用户的比赛ID进行排序,然后计算相邻比赛ID的差值(或使用窗口函数计算连续的比赛ID序列)。 判断连续三场:我们可以用窗口函数,计算当前行与前两行的比赛ID,如果满足当前比赛ID=前一行比赛ID+1,且前一行比赛ID=前两行比赛ID+1,则这三场连续。 或者,使用contest_id - row_number() over(partition by user_id order by contest_id) as diff,如果diff相同,则说明是连续的。 步骤3:根据步骤2,对每个用户分组,统计是否存在连续三个或以上的比赛ID,如果有则满足条件1。 但是,题目中比赛ID是连续的,且没有跳过,所以我们可以使用第二种方法(差值法)来标记连续区间。 然而,题目要求的是连续三场及更多,所以我们只需要检查是否存在连续三个比赛ID即可。 另外,条件2可以直接用一个子查询完成。 最后,将满足条件1或条件2的用户合并,然后关联Users表获取name和mail。 注意:同一个用户可能同时满足两个条件,需要去重。 我们来逐步构建SQL: 1. 创建包含所有奖牌获得者的列表(每个用户参加的比赛及其比赛ID): (select contest_id, gold_medal as user_id from Contests union all select contest_id, silver_medal from Contests union all select contest_id, bronze_medal from Contests ) t1 注意:这里使用union all因为每个奖牌都是独立的,并且同一个用户可能在不同比赛中多次获奖,同一场比赛同一个用户不会重复(因为一场比赛一个用户只能获得一个奖牌),所以这里不需要去重。 2. 条件2:获得至少三枚金牌的用户 select gold_medal from Contests group by gold_medal having count(*)>=3 3. 条件1:连续三场比赛都获奖的用户 从t1中,我们需要按用户分组,然后为每个用户生成按比赛ID排序的序列,然后判断是否存在连续三个比赛ID。 方法:使用窗口函数,对每个用户的比赛ID排序,然后计算比赛ID与行号的差值(如果比赛ID连续,则差值相同)。然后统计同一个差值连续出现的次数,如果次数>=3,则说明有一段连续区间长度>=3。 具体步骤: with t2 as ( select user_id, contest_id, contest_id - row_number() over (partition by user_id order by contest_id) as diff from t1 ) select user_id from t2 group by user_id, diff having count(*) >= 3 这个查询会找出至少连续三场比赛获奖的用户。 4. 合并条件1和条件2的用户:union distinct(因为可能重复) 5. 关联Users表,获取name和mail。 但是,注意:题目中的连续三场是指比赛ID连续的三场比赛,并且用户在这三场比赛中都获奖(每场至少一次)。我们的t1表已经包含了用户在任何一场比赛中获奖的记录,然后我们按用户分组计算连续区间,这样是合理的。 但是,有一个问题:同一个用户可能在同一场比赛获得多个奖牌吗?不会,因为一场比赛的金银铜牌都是不同的用户。 因此,我们可以这样写: 优化:上面的t1表可能会很大,我们可以先对t1按user_id和contest_id去重(虽然理论上去重不是必须的,因为同一个用户在同一场比赛不会重复,但为了保险,我们可以用distinct或者group by,但实际上不需要,因为union all后同一个用户同一场比赛只有一条记录)。 但是,我们也可以不去重,因为确实不会重复。 完整SQL如下: 注意:由于题目描述的比赛ID是连续的,所以我们可以使用这种方法。 但是,还有一种情况:同一个用户可能有多个连续的区间,但我们只需要知道至少有一个连续区间长度>=3即可。 因此,我们可以这样写: 步骤1:创建所有获奖记录 步骤2:条件1(连续三场获奖)使用上述的差值法 步骤3:条件2(至少三块金牌) 步骤4:合并两个条件的用户ID 代码: 注意:在条件1的查询中,我们使用了差值法,但需要注意,如果用户有多个不连续的区间,我们只需要有一个区间满足连续>=3即可。而差值法分组后,每个分组就是一个连续区间,然后我们统计每个连续区间的长度,只要有一个>=3,我们就取这个用户。 因此,我们可以在条件1的查询中使用having count(*)>=3,然后取distinct user_id。 但是,我们也可以不用分组,而是用窗口函数直接判断连续情况。但是差值法效率较高。 另外,注意:如果连续区间长度为4,那么差值相同的分组内会有4行,所以分组后计数>=3即可。 因此,我们这样编写: with all_prize as ( select contest_id, gold_medal as user_id from Contests union all select contest_id, silver_medal from Contests union all select contest_id, bronze_medal from Contests ), consecutive_users as ( select distinct user_id from ( select user_id, contest_id, contest_id - row_number() over (partition by user_id order by contest_id) as diff from all_prize ) t group by user_id, diff having count(*) >= 3 ), gold_users as ( select gold_medal as user_id from Contests group by gold_medal having count(*) >= 3 ), candidate_users as ( select user_id from consecutive_users union select user_id from gold_users ) select u.name, u.mail from candidate_users c join Users u on c.user_id = u.user_id; 但是,这里有一个潜在问题:all_prize表可能会很大(因为三列合并,行数是3倍于比赛场次),但是比赛场次不会太多(通常不会超过几百场),所以可以接受。 但是,我们还可以优化:在计算连续区间之前,我们可以先按user_id, contest_id去重(虽然理论不需要,但是为了逻辑清晰,可以加)。但这里不需要,因为同一个用户在同一场比赛不会重复。 测试:如果有一个用户,他参加了比赛1,2,3(连续三场)并且都获奖,那么他的contest_id序列是1,2,3。 行号:1,2,3(按contest_id排序) 差值:1-1=0, 2-2=0, 3-3=0,所以diff都是0,分组后count(*)=3,满足条件。 如果用户参加了比赛1,2,4,那么序列为1,2,4(排序后),行号分别为1,2,3,差值为0,0,1(4-3=1)。那么分组: diff=0: 比赛1,2 -> 计数2 diff=1: 比赛4 -> 计数1 都不满足>=3。 所以这个用户不会被选入条件1。 如果用户有比赛1,2,3,5,那么连续区间1,2,3(计数3)满足条件。 所以这个方法是正确的。 但是,我们也可以考虑另一种方法:使用自连接来查找连续三场。但是自连接在数据量大时效率低(比如比赛场次多,用户多时,自连接会生成很大的中间表)。而上面的窗口函数方法只需要扫描一次数据。 因此,我们采用窗口函数的方法。 另外,条件2也可以使用all_prize表,但条件2只要求金牌,所以直接对Contests表的gold_medal分组计数即可。 最终,我们输出name和mail。 注意:用户表中可能包含没有获奖的用户,而我们只关心候选用户,所以使用inner join。 但是,如果候选用户不在Users表中?题目没有说明,但按照逻辑,获奖用户的user_id应该在Users表中,所以inner join是安全的。 因此,以上SQL可以满足要求。 但是,我们还需要注意一种情况:同一个用户可能同时满足两个条件,使用union会自动去重。 所以,这个查询是正确的。 我们也可以考虑使用exists来避免生成临时表,但这里使用CTE更清晰。 现在,我们编写最终SQL。 注意:在MySQL 8.0+支持CTE和窗口函数。 如果环境不是MySQL 8.0,则需要使用其他方法,但题目没有说明,我们假设环境支持。 因此,最终SQL如下: </think>要查询满足以下任一条件的用户姓名和邮件:(1)在**连续三场及以上比赛**中赢得任意奖牌;(2)在**三场及以上不同比赛**中赢得金牌,可通过以下SQL实现: ### 完整解决方案 ```sql WITH -- 步骤1:合并所有获奖记录(金奖/银奖/铜奖) all_prizes AS ( SELECT contest_id, gold_medal AS user_id FROM Contests UNION ALL SELECT contest_id, silver_medal FROM Contests UNION ALL SELECT contest_id, bronze_medal FROM Contests ), -- 步骤2:识别连续三场获奖用户 consecutive_winners AS ( SELECT DISTINCT user_id FROM ( SELECT user_id, contest_id - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY contest_id) AS grp FROM all_prizes ) t GROUP BY user_id, grp HAVING COUNT(*) >= 3 -- 连续区间长度≥3 ), -- 步骤3:识别三场及以上金牌用户 gold_winners AS ( SELECT gold_medal AS user_id FROM Contests GROUP BY gold_medal HAVING COUNT(*) >= 3 -- 非连续金牌≥3 ), -- 步骤4:合并候选人ID candidates AS ( SELECT user_id FROM consecutive_winners UNION SELECT user_id FROM gold_winners ) -- 步骤5:关联用户表获取详细信息 SELECT u.name, u.mail FROM candidates c JOIN Users u ON c.user_id = u.user_id; ``` ### 关键逻辑解析 1. **所有获奖记录整合** (`all_prizes`) - 通过`UNION ALL`合并三列奖牌字段,生成`(contest_id, user_id)`元组 - 包含每个用户在每场比赛的获奖记录(无论奖牌类型) 2. **连续获奖检测** (`consecutive_winners`) - 核心技巧:`contest_id - ROW_NUMBER() AS grp` - 数学原理:连续比赛的`grp`值恒定(如比赛[1,2,3]的grp=0)[^3] - `HAVING COUNT(*) >= 3`筛选连续区间≥3的用户 3. **金牌次数统计** (`gold_winners`) - 直接分组统计`gold_medal`出现次数 - 不要求比赛连续,只需总次数≥3[^1] 4. **结果合并与去重** (`candidates`) - `UNION`自动去重同一用户满足多条件的情况 ### 性能优化说明 | 组件 | 优化策略 | 优势 | |--------------|----------------------------------|--------------------------| | `all_prizes` | 使用`UNION ALL`代替`UNION` | 避免不必要的去重开销 | | 连续检测 | 差值法替代自连接 | 时间复杂度$O(n)$ vs $O(n^3)$[^2] | | 金牌统计 | 直接聚合原始表 | 减少中间表数据量 | ### 进阶思考
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值