小游戏叒AK了!

10 篇文章 0 订阅

题目

传送门 to luogu

题目背景
今天的比赛又是小游戏 A K AK AK 的万千比赛中平淡无奇的一场。

本来他听说最近一个叫 t ⋯ i s t \sf t\cdots ist tist 的白俄罗斯人挺嚣张的,就准备出一道题来震慑他。但是 D D ( X Y X ) \sf DD(XYX) DD(XYX) 还有校董会要开,所以他打消了这个念头,开始准备他的讲稿。

题目描述
校董会上, D D ( X Y X ) \sf DD(XYX) DD(XYX) 要大家猜他每天都 A K AK AK 了几场比赛。已知第 i i i 天是 a i a_i ai 场,一共 n n n 天,满足 1 ⩽ a i ⩽ n 1\leqslant a_i\leqslant n 1ain 且任意两天的 A K AK AK 场数不相同。

你只可以问他两种问题(为什么?因为校长就是这么规定的):

  • 询问 a x   m o d   a y a_x\bmod a_y axmoday 的值。注意你必须满足 a x > a y a_x>a_y ax>ay,否则 a x a_x ax 就直接暴露了,不符合 D D ( X Y X ) \sf DD(XYX) DD(XYX) 装弱麻痹对手的原则;
  • 询问他有多少天打了不小于 p p p 场比赛,来评估他对学校管理有多不在乎。也即 { i    ∣    a i ⩾ p ,    i ∈ S } \{i\;|\;a_i\geqslant p,\;i\in S\} {iaip,iS},其中 p ∈ [ 1 , n ] p\in[1,n] p[1,n] 为你给定的参数, S S S 为你给定的集合。注意询问的是至少打 p p p 场比赛,而 a i a_i ai A K AK AK 场数。

现在,你可以问 m 1 m_1 m1 a x   m o d   a y a_x\bmod a_y axmoday 的值,和 m 2 m_2 m2 次集合内不小于 p p p 的数,但是要满足 ∑ ∣ S ∣ ⩽ m 3 \sum|S|\leqslant m_3 Sm3

请你猜出 D D ( X Y X ) \sf DD(XYX) DD(XYX) 每天的 A K AK AK 场数吧!话说我为什么要猜这个?

数据范围与提示
对于一组数据,满足 n = m 1 = m 3 = 4 n=m_1=m_3=4 n=m1=m3=4 m 2 = 1 m_2=1 m2=1

对于其余数据,限制最强的满足 n = m 1 = 1 3 m 3 = 5 × 1 0 4 n=m_1=\frac{1}{3}m_3=5\times 10^4 n=m1=31m3=5×104 m 2 = 15 m_2=15 m2=15

思路

发现 m 2 < log ⁡ 2 n m_2<\log_2 n m2<log2n,考虑二分?找到了 n 2 \frac{n}{2} 2n,那么所有 x > n 2 x>\frac{n}{2} x>2n 都去取模它便可知晓。

但是我们需要两次 m 2 m_2 m2 询问,问 ⩾ n 2 \geqslant\frac{n}{2} 2n ⩾ n 2 + 1 \geqslant\frac{n}{2}+1 2n+1 的,找到对称差。这样 m 2 = 2 log ⁡ 2 n m_2=2\log_2n m2=2log2n,完全过不了。

优化方式我确实没想到。出发点是, m 1 = n m_1=n m1=n 则基本上每次 m 1 m_1 m1 询问都可以唯一确定一个数。

显然 x   m o d   n 2    ( n ⩾ x > n 2 ) x\bmod\frac{n}{2}\;(n\geqslant x>\frac{n}{2}) xmod2n(nx>2n) 是可以的。而你有没有想过反过来, n   m o d   x    ( n 2 ⩽ x < n ) n\bmod x\;(\frac{n}{2}\leqslant x<n) nmodx(2nx<n) 也是可以的?

只要想到这一点,我们可以最初 m 2 m_2 m2 询问一次确定 n n n 的位置,然后 m 1 m_1 m1 询问确定了 [ n 2 , n ) [\frac{n}{2},n) [2n,n) 的位置,然后以 n 2 \frac{n}{2} 2n 作为新的最大值,递归。

这样一来 m 1 = ⌈ log ⁡ 2 n ⌉ + 1 m_1=\lceil\log_2n\rceil+1 m1=log2n+1,还是稍微多一点,怎么办?

计算一下发现,经过 m 2 = 15 m_2=15 m2=15 次询问, n n n 恰好为 4 4 4 。现在我们只有 m 1 m_1 m1 询问,可以做吗?

事实上完全可以。因为 4   m o d   x    ( 1 ⩽ x ⩽ 3 ) 4\bmod x\;(1\leqslant x\leqslant 3) 4modx(1x3) 可以找到 3 3 3(唯一余数为 1 1 1 的数字),接下来的一次询问可以确定 1 1 1 或者 2 2 2,那么最后剩一个空位直接填好。

最后计算一下 m 3 m_3 m3 。第一次用 n n n 问出 n n n 的位置,接下来是 n + n 2 + n 4 + ⋯ + 1 = 2 n n+\frac{n}{2}+\frac{n}{4}+\cdots+1=2n n+2n+4n++1=2n,恰好是 3 n 3n 3n

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(int x){
	if(x > 9) writeint(x/10);
	putchar((x-x/10*10)^48);
}

const int MaxN = 50005;

int n, m1, m2, m3;
int a[MaxN];
void solve(int m,int loca){
	a[loca] = m; // mark it
	if(m == 1) return ;
	if(m == 2)
		rep(i,1,n) if(!a[i]){
			a[i] = 1; return ;
		}
	if(m == 4){
		rep(i,1,n) if(!a[i]){
			putchar('!'), putchar(' ');
			writeint(loca), putchar(' ');
			writeint(i), putchar('\n');
			fflush(stdout);
			if(readint() == 1){
				a[loca = i] = 3; break;
			}
		}
		int lst = 0;
		rep(i,1,n) if(!a[i]){
			if(lst){
				a[i] = 3-lst; break;
			}
			putchar('!'), putchar(' ');
			writeint(loca), putchar(' ');
			writeint(i), putchar('\n');
			fflush(stdout);
			lst = a[i] = readint()+1;
		}
		return ;
	}

	int p = (m+1)>>1; // boundary
	putchar('?'), putchar(' ');
	writeint(m-1); // except m
	rep(i,1,n) if(!a[i])
		putchar(' '), writeint(i);
	putchar(' '), writeint(p);
	putchar('\n'); fflush(stdout);
	for(int k=readint(); k; --k)
		a[readint()] = p; // put tag

	int id = 0; // next location
	rep(i,1,n) if(a[i] == p){
		putchar('!'), putchar(' ');
		writeint(loca), putchar(' ');
		writeint(i), putchar('\n');
		fflush(stdout);
		a[i] = m-readint();	
		if(a[i] == m) a[i] = p;
		if(a[i] == p) id = i;
	}

	solve(p,id); // remaining
}

int main(){
	n = readint(), m1 = readint();
	m2 = readint(), m3 = readint();
	printf("? %d ",n);
	rep(i,1,n) writeint(i), putchar(' ');
	writeint(n), putchar('\n');
	fflush(stdout); readint(); // count = 1
	solve(n,readint()), putchar('A');
	rep(i,1,n) putchar(' '), writeint(a[i]);
	putchar('\n'); fflush(stdout);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值