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

Description

给定一个长度为 n n n 的首项为 s s s 且公差为 y y y 的等差数列。

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

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

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

Solution

Subtask 1

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

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

期望得分 5 5 5 分。

Subtask 2

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

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

人口普查分到此为止。

Subtask 3

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

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

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

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

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

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

期望得分 100 100 100 分。

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;
}

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/119810341
今日推荐