Doremy‘s Perfect DS Class (Easy Version)

题目描述

The only difference between this problem and the other two versions is the maximum number of queries. In this version, you are allowed to ask at most \mathbf{30}30 queries. You can make hacks only if all versions of the problem are solved.

This is an interactive problem.

"Everybody! Doremy's Perfect Data Structure Class is about to start! Come and do your best if you want to have as much IQ as me!" In today's Data Structure class, Doremy is teaching everyone a powerful data structure — Doremy tree! Now she gives you a quiz to prove that you are paying attention in class.

Given an array aa of length mm , Doremy tree supports the query Q(l,r,k)Q(l,r,k) , where 1 \leq l \leq r \leq m1≤l≤r≤m and 1 \leq k \leq m1≤k≤m , which returns the number of distinct integers in the array \left[\lfloor\frac{a_l}{k} \rfloor, \lfloor\frac{a_{l+1}}{k} \rfloor, \ldots, \lfloor\frac{a_r}{k} \rfloor\right][⌊kal​​⌋,⌊kal+1​​⌋,…,⌊kar​​⌋] .

Doremy has a secret permutation pp of integers from 11 to nn . You can make queries, in one query, you give 33 integers l,r,kl,r,k ( 1 \leq l \leq r \leq n1≤l≤r≤n , 1 \leq k \leq n1≤k≤n ) and receive the value of Q(l,r,k)Q(l,r,k) for the array pp . Can you find the index yy ( 1 \leq y \leq n1≤y≤n ) such that p_y=1py​=1 in at most \mathbf{30}30 queries?

Note that the permutation pp is fixed before any queries are made.

输入格式

输出格式

You begin the interaction by reading an integer nn ( 3 \le n \le 10243≤n≤1024 ) in the first line — the length of the permutation.

To make a query, you should output

  • "? l\ r\ kl r k " ( 1 \leq l \leq r \leq n1≤l≤r≤n , 1 \leq k \leq n1≤k≤n )

in a separate line. After each query, you should read an integer xx — the value of Q(l,r,k)Q(l,r,k) for pp . In this version of the problem, you can make at most 3030 such queries.To give the answer, you should output

  • "! yy " ( 1 \leq y \leq n1≤y≤n )

in a separate line, where p_y=1py​=1 .After printing a query or the answer, do not forget to output the end of line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:

  • fflush(stdout) or cout.flush() in C++;
  • System.out.flush() in Java;
  • flush(output) in Pascal;
  • stdout.flush() in Python;
  • see documentation for other languages.

Hacks Format

The first line of the hack contains an integer nn ( 3 \le n \le 10243≤n≤1024 ) — the length of the permutation.

The second line of the hack contains nn distinct integers p_1,p_2,\ldots,p_np1​,p2​,…,pn​ ( 1 \le p_i\le n1≤pi​≤n ) — the permutation.

题意翻译

  • 这是一道交互题。
  • 交互库有一个 [1,n][1,n] 的排列 pp。
  • 你可以询问 l,r,kl,r,k,交互库会返回 \left\lfloor\dfrac{p_l}k\right\rfloor,\left\lfloor\dfrac{p_{l+1}}k\right\rfloor,\cdots,\left\lfloor\dfrac{p_r}k\right\rfloor⌊kpl​​⌋,⌊kpl+1​​⌋,⋯,⌊kpr​​⌋ 中不同数的个数。
  • 你需要在 3030 次询问内找到 pp 中 11 的位置。
  • n\in[3,1024]n∈[3,1024]。

输入输出样例

输入 #1复制

5

2

2

1

3

输出 #1复制

? 1 3 4

? 3 5 3

? 3 4 5

? 3 5 2

! 4

说明/提示

The permutation in the example is [3,5,2,1,4][3,5,2,1,4] .

The input and output for example illustrate possible interaction on that test (empty lines are inserted only for clarity).

In this interaction process:

  • For the first query, \lfloor\frac{3}{4}\rfloor=0,\lfloor\frac{5}{4}\rfloor=1,\lfloor\frac{2}{4}\rfloor=0⌊43​⌋=0,⌊45​⌋=1,⌊42​⌋=0 , so the answer is 22 .
  • For the second query, \lfloor\frac{2}{3}\rfloor=0,\lfloor\frac{1}{3}\rfloor=0,\lfloor\frac{4}{3}\rfloor=1⌊32​⌋=0,⌊31​⌋=0,⌊34​⌋=1 , so the answer is still 22 .
  • For the third query, \lfloor\frac{2}{5}\rfloor=0,\lfloor\frac{1}{5}\rfloor=0⌊52​⌋=0,⌊51​⌋=0 , so the answer is 11 .
  • For the fourth query, \lfloor\frac{2}{2}\rfloor=1,\lfloor\frac{1}{2}\rfloor=0,\lfloor\frac{4}{2}\rfloor=2⌊22​⌋=1,⌊21​⌋=0,⌊24​⌋=2 , so the answer is 33 .

The correct answer is got after 44 queries, so this process will be judged correct.

-> CF官方题解

题意简述

交互题。有一个排列 p_1,...,p_np1​,...,pn​, 你需要在 3030 次以内的询问后找出 11 的位置。每一次询问输出 ?\ l\ r\ k? l r k, 表示询问 \lfloor {p_l\over k} \rfloor,...,\lfloor {p_{r}\over k} \rfloor⌊kpl​​⌋,...,⌊kpr​​⌋ 中有多少个不同的数。

题目分析

首先发现 k=1k=1 的询问是一点用都没有的,不用考虑。而 k\geq2k≥2 时,\lfloor {1\over k}\rfloor⌊k1​⌋ 总是 00, 为了让 11 与其它数区分开,我们考虑进行 k=2k=2 的询问,因为这样能让其它数除以 kk 下取整后不为 00。

当 nn 为奇数时,除了 11 以外的所有数除以 22 下取整后总是两两配对。因此当我们把 pp 分成两段分别询问时,两段中除 11 以外的未配对数的数量总是相等,所以 11 就应该在未配对数较多的一段中。采用二分的方法,最多询问 2\times\log(1024)=202×log(1024)=20 次,满足本题要求(甚至满足了 G3 的要求)。

而当 nn 为偶数时,有 11 与 nn 两个数没有配对,因此当询问的两段中未配对数相等时就很不好处理。但我们发现 nn 有个更特殊的性质,只有 \lfloor {n\over n} \rfloor⌊nn​⌋ 为 11, 因此只需要在这时进行一次 k=nk=n 的询问就能找到 11 的位置了。同样用二分,最多询问 3\times\log(1024)=303×log(1024)=30 次,满足本题要求。

在 k=nk=n 的询问里,要注意不能询问长度为 11 的一段,否则起不到判断的作用,记得特判。

进一步的优化请看 G2 和 G3 的题解。

代码

#include <iostream>
#include <ctime>
#include <cstdio>
#include <cmath>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
#include <map>
#include <stdlib.h>
using namespace std;
int n;
inline int read()
{
	int now=0,nev=1; char c=getchar();
	while(c<'0' || c>'9') { if(c=='-') nev=-1; c=getchar();}
	while(c>='0' && c<='9') { now=(now<<1)+(now<<3)+(c&15); c=getchar(); }
	return now*nev;
}
int ask(int l,int r,int k)
{
	printf("? %d %d %d\n",l,r,k);
	fflush(stdout);
	return read();
}
void putans(int x)
{
	printf("! %d\n",x);
	fflush(stdout);
}
bool check1(int x)
{
	int a=ask(1,x,2);
	int b=0;
	if(x+1<=n)
	{
		b=ask(x+1,n,2);
	}
	int c=x-2*(x-a);
	int d=(n-x)-2*((n-x)-b);
	if(c>d)
	{
		return 1;
	}
	return 0;
}
void solve1()//n为奇数 
{
	int l=1,r=n;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(check1(mid))
		{
			r=mid;
		}
		else
		{
			l=mid+1;
		}
	}
	putans(l);
}
bool check2(int x)
{
	int a=ask(1,x,2);
	int b=0;
	if(x+1<=n)
	{
		b=ask(x+1,n,2);
	}
	int c=x-2*(x-a);
	int d=(n-x)-2*((n-x)-b);
	if(c>d)
	{
		return 1;
	}
	else if(c<d)
	{
		return 0;
	}
	else//两段未配对的数数量相等 
	{
		int e=0;
		if(x>1)//放在出现ask(1,1,n)的无效情况 
		{
			e=ask(1,x,n);
		}
		else
		{
			e=3-ask(x+1,n,n);
		}
		if(e==2)
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
}
void solve2()//n为偶数 
{
	int l=1,r=n;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(check2(mid))
		{
			r=mid;
		}
		else
		{
			l=mid+1;
		}
	}
	putans(l);
}
int main()
{
	n=read();
	if(n%2==1)
	{
		solve1();
	}
	else
	{
		solve2();
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值