[Wdoi 2021 & Round 3] 夜雀 cooking 题解

本文探讨了一个关于等差数列的问题,其中部分位置随机染成紫色。文章介绍了三种子任务的解决方案,包括快速预处理、二分查找紫色位置和利用紫色均匀性进行分块查询。关键在于利用紫色分布特性判断紫色数量并定位它们。

Description

给定一个长度为 nnn 的首项为 sss 且公差为 yyy 的等差数列。

数列中的所有位置初始为蓝色。不过,一个可爱的妹子在其中等概率随机mmm 个位置并将它们染成了紫色。显然,紫是均匀分布的

现在你想要知道她染了哪些位置,因此你可以提出询问。每个询问格式形如 l r,表示查询 [l,r][l,r][l,r] 中所有蓝色位置的权值和。当你想要回答的时候,先输出 −1-11,再输出 mmm 个从小到大排列的数表示这些紫色的位置。

TTT 次询问,当你的询问次数不超过 200T200T200T 时可以获得满分。1≤n≤105,1≤m≤1001 \le n \le 10^5,1 \le m \le 1001n105,1m100

Solution

Subtask 1

首先预处理出整个序列,并求出序列中所有数的和。

查询 1 n 即可得到所有蓝色位置的权值和。显然,用所有数的和减去所有蓝色数的和就是唯一紫色位置的权值。得到这个权值后就能直接确定紫的位置啦。

期望得分 555 分。

Subtask 2

考虑执行 mmm 次二分得到所有的位置。

单次查询次数严格不超过 O(m⌈log⁡n⌉)=O(1700)O(m \lceil \log n \rceil)=O(1700)O(mlogn)=O(1700),期望得分 242424 分。

人口普查分到此为止。

Subtask 3

题目中提到紫是均匀的,这启发我们对数列进行分块——共分成 mmm 块,每块大小 nm\frac n mmn,那么每个块内期望只有 1 个紫。不过,这只是期望而已,在更多的情况中,可能会有一些块中有 000 个紫,222 个紫,甚至 3,4,53,4,53,4,5 个紫。

现在,问题在于: 判断块内是否只有 111 个紫,找到这个紫,以及如果超过 111 个紫的时候我们该怎么办。

首先,考虑如何判断是否只有 111 个紫。通过手玩不难发现,在第 2,3,⋯ ,m2,3,\cdots,m2,3,,m 个块内,若该块中紫色的数量超过 111,那么蓝的权值和一定严格小于紫的数量恰为 000111 的情况。具体来说,令这个块对应区间 [l,r][l,r][l,r],那么必定有 al+al+1>ara_l+a_{l+1}>a_ral+al+1>ar,因此若只有 111 个紫那么蓝的和不小于 s−ars-a_rsar,若大于 111 个紫那么蓝的和不大于 s−al−al+1s-a_l-a_{l+1}salal+1,然而 al+al+1>ara_l+a_{l+1}>a_ral+al+1>ar,所以前者永远大于后者。

从而,我们只需要查询 [l,r][l,r][l,r] 中蓝的和就可以判断了。同时,作为副产品,我们也能够确定该块内唯一的紫所在的位置。

特别的,对于第一个块,我们需要套用 Subtask 2 进行二分;因为紫色均匀,所以该块内的紫的数量很少,二分的次数并不多。浴室,前两个任务被我们一鼓作气地解决啦!

最后,考虑如果该块内超过 111 个紫我们该怎么办呢?考虑通过分治将其归结为 111 个紫的情况。我们先查询整个 [l,r][l,r][l,r],然后查询 [l,mid][l,mid][l,mid],接着 [mid+1,r][mid+1,r][mid+1,r] ⋯⋯\cdots \cdots 若可以确定当前区间内只有 111 个紫,那么我们直接返回。可以发现,由于随机的性质,块内的紫不多且分布均匀,不会缩在一起,所以期望的查询次数并不大。细节方面,我们可以执行线段树上二分。

期望得分 100100100 分。

Code

出题人真毒瘤,卡了 long long\text{long long}long long,换成 unsigned long long\text{unsigned long long}unsigned long long 才过。果然不写不知道,写了吓一跳啊。

#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
const int maxl=200005;

int read(){
	int s=0,w=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-')  w=-w;ch=getchar();}
	while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
	return s*w;
}
int t,n,m,s,k;
int a[maxl],pre[maxl],tree[maxl<<2];
bool b[maxl];

void pushup(int rt){tree[rt]=tree[rt<<1]+tree[rt<<1|1];}
void build_tree(int l,int r,int rt){
	if (l==r){
		tree[rt]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build_tree(l,mid,rt<<1),build_tree(mid+1,r,rt<<1|1);
	pushup(rt);
}
int Sum(int x,int y){return pre[y]-pre[x-1];}
int query(int l,int r,int rt,int x){
	if (!x){
		cout<<l<<' '<<r<<endl;
		cin>>x;
	}
	if (Sum(l,r)==x)  return x;
	else if (Sum(l,r)-x<=a[r]){
		int original_x=x;
		x=Sum(l,r)-x,x=(x-s)/k+1;
		b[x]=1;
		return original_x;
	}
	int mid=(l+r)>>1,lson_sum;
	lson_sum=query(l,mid,rt<<1,0);
	query(mid+1,r,rt<<1|1,x-lson_sum);
	
	return x;
}
//first
bool chk(int le,int ri){
	cout<<le<<' '<<ri<<endl;
	int x;
	cin>>x;
	x=pre[ri]-pre[le-1]-x;
	
	return x;
}
int get_nxt(int now,int r){
	int le=now,ri=r,res=r+1;
	while (le<=ri){
		int mid=(le+ri)>>1;
		if (chk(now,mid))  res=mid,ri=mid-1;
		else le=mid+1;
	}
	return res;
}
void Binary_search(int l,int r){
	int now=l,cnt=0;
	while (now<=r){
		int v=get_nxt(now,r);
		if (v<=r)  b[v]=1,cnt++,now=v+1;
		if (cnt==m||v>r)  break;
	}
}

signed main(){
	t=read();
	while (t--){
		n=read(),m=read(),s=read(),k=read();
		a[1]=s;
		for (int i=2;i<=n;i++)  a[i]=a[i-1]+k;
		for (int i=1;i<=n;i++)  b[i]=0,pre[i]=pre[i-1]+a[i];
		for (int i=1;i<=n;){
			int l=i,r=min(i+(n/m)-1,n);
			build_tree(l,r,1);
			if (i!=1)  query(l,r,1,0);
			else Binary_search(l,r);
			i=r+1;
		}
		printf("-1 ");
		for (int i=1;i<=n;i++){
			if (b[i])  printf("%d ",i);
		}
		cout<<endl;
	}
	return 0;
}
&lt;think&gt;好的,我现在要解决的是吉林省级程序设计竞赛的F题&ldquo;Cooking&rdquo;的题解。首先,我需要理解题目要求。根据用户提供的引用内容,题目可能涉及高教练需要同时烹饪多道菜,展示他的烹饪技巧。这可能涉及到时间安排、并行处理或者资源分配的问题。 接下来,我需要分析可能的解题思路。假设题目要求是在最短时间内完成所有菜肴的烹饪,且每道菜有不同的烹饪时间,而高教练可以同时处理多个任务。这种情况下,问题可能转化为任务调度问题,比如寻找最优的并行处理方案。 例如,如果每个炉子可以同时烹饪一道菜,但每个菜可能需要不同的时间,那么问题可能需要计算如何分配这些菜到不同的炉子,使得总时间最短。这类似于多处理器调度问题,属于NP难问题,可能需要贪心算法或动态规划来解决。 另一种可能性是,题目涉及到数学公式或概率问题,比如引用中提到的费马测试或质数判断,但根据&ldquo;Cooking&rdquo;这个标题,可能更多是任务调度。不过需要结合用户提供的引用内容中的其他线索,比如提到的加密算法,但暂时可能不相关。 假设题目具体是:高教练有M个炉子,每个炉子可以同时烹饪一道菜,共有N道菜,每道菜需要不同的时间。如何安排使得总时间最短?此时,解法可能类似于将任务分配到多个处理器,使得最长处理时间的炉子的时间尽可能短。这通常使用贪心算法,比如将最长任务优先分配,以平衡负载。 或者,可能涉及到时间窗口的安排,例如每道菜需要在特定时间点进行操作,比如烤箱的使用,需要避免冲突。这种情况下可能需要区间调度或时间重叠的处理。 现在,我需要验证这个假设是否合理。用户提供的引用[1]提到高教练能同时烹饪M道菜,说明可能有M个并行处理单元。因此,题目可能是给定N道菜,每道菜烹饪时间为t_i,如何分配到M个炉子上,使得总完成时间最短。这确实是典型的多机调度问题。 对于多机调度问题,最优解法是NP难的,但在竞赛中通常使用贪心算法,如最长处理时间优先(LPT),即先将任务按降序排列,然后依次将每个任务分配给当前总时间最小的机器。 因此,解题步骤可能是: 1. 将所有的菜按烹饪时间从大到小排序。 2. 使用优先队列(最小堆)来维护每个炉子的当前总时间。 3. 每次取出堆顶(当前总时间最小的炉子),将当前最大的任务分配给它,更新该炉子的总时间并重新插入堆中。 4. 最后,堆中的最大值即为所需的最短总时间。 例如,假设M=3个炉子,N=6道菜,时间分别为[5,5,4,3,2,2],按降序排列后,依次分配: 第一个5给炉子1(总时间5),第二个5给炉子2(总时间5),第三个4给炉子3(总时间4)。第四个3分配给当前最小的炉子3(4+3=7),第五个2给炉子1(5+2=7),第六个2给炉子2(5+2=7)。最终最长是7。 这样总时间为7,这确实是正确解。 因此,代码实现可以用优先队列(最小堆)来处理。在Python中,可以使用heapq模块。 接下来需要编写代码,处理输入,排序,然后分配任务。 可能的输入格式是,第一行给出N和M,接着是N个整数表示每道菜的时间。输出总的最短时间。 例如,输入: 6 3 5 5 4 3 2 2 输出应为7。 代码的大体结构如下: 读取N和M,读取时间列表,降序排序。初始化一个最小堆,初始时每个炉子的总时间为0,但如果有M个炉子,初始堆应该放入M个0。然后,遍历每个时间t,取出堆中最小的总时间,加上t,再放回堆中。最后,堆中的最大值即为答案。 需要注意的是,当M &gt;= N时,总时间就是最大的t_i,因为每个菜可以单独一个炉子。 因此,Python代码可能如下: import heapq n, m = map(int, input().split()) times = list(map(int, input().split())) times.sort(reverse=True) if m &gt;= n: print(max(times)) else: heap = [] for _ in range(m): heapq.heappush(heap, 0) max_time = 0 for t in times: current = heapq.heappop(heap) current += t if current &gt; max_time: max_time = current heapq.heappush(heap, current) print(max_time) 这样应该能得到正确的结果。需要测试几个案例,例如上面的例子是否正确。 另外,可能存在其他情况,比如当所有时间相同,或者M=1的情况。例如,当M=1时,总时间就是所有时间之和。 综上,这个思路应该是正确的,代码也应当能处理这些情况。&lt;/think&gt;### 吉林省级程序设计竞赛 F题 &quot;Cooking&quot; 题解 #### 问题分析 题目要求高教练在多个炉子上并行烹饪多道菜,目标是找到最短完成时间。问题本质是**多机调度问题**,需将$N$道菜分配到$M$个炉子上,使得最大炉子总时间最小。该问题属于NP难问题,但可通过贪心算法(最长处理时间优先,LPT)高效逼近最优解[^2]。 #### 算法思路 1. **排序处理**:将菜品按烹饪时间降序排列,优先处理耗时长的任务。 2. **动态分配**:使用最小堆维护各炉子的当前总时间,每次选择当前负载最小的炉子分配任务。 3. **时间复杂度**:排序$O(N \log N)$ + 堆操作$O(N \log M)$,总体效率满足竞赛要求。 #### 代码实现 ```python import heapq n, m = map(int, input().split()) times = list(map(int, input().split())) times.sort(reverse=True) # 降序排序 if m &gt;= n: print(max(times)) # 炉子数量足够,直接取最大值 else: heap = [] # 初始化M个炉子,初始总时间均为0 for _ in range(m): heapq.heappush(heap, 0) max_time = 0 for t in times: current = heapq.heappop(heap) # 取出当前总时间最小的炉子 current += t # 分配当前菜品 if current &gt; max_time: max_time = current heapq.heappush(heap, current) # 更新炉子总时间 print(max_time) ``` #### 示例测试 **输入:** ``` 6 3 5 5 4 3 2 2 ``` **输出:** ``` 7 ``` **解释**:炉子分配为$[5+2=7, 5+2=7, 4+3=7]$,最大时间为7。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值