CF1368F

CF1368F

这是一道交互题。

Alice 和 Bob 在玩游戏,有 \(n\) 盏灯排成一个圈,顺时针标号为 \(1\sim n\),初始所有灯都是关着的。

每轮操作为:

  • Alice 选择一个 \(k\in [1,n]\) 然后打开任意 \(k\) 盏灯(可以重复打开)
  • Bob 关闭一段长度为 \(k\) 的区间的灯。

\(R\) 表示经过若干轮后开着的灯的数量的最大值。

你需要在不超过 \(10^4\) 次操作达到这个上界。

\(n\le 1000\)

  • 请注意,灯是环形摆放。

Solution

\(A\) 表示当前亮着的灯的数量。

假设当前增加了 \(k\) 盏灯,里面如果 Bob 无法消去 \(k\) 盏灯,那么 \(A\) 将会增大。

换而言之,如果操作后不存在连续的 \(k\) 盏灯,那么当前操作就可以使得 \(A\) 增大。

考虑 \(A\) 能够增大的必要条件是什么,即存在一个 \(k\) 使得操作后有不存在连续的 \(k\) 盏灯。

注意到 \(R<n\),所以我们至少有一盏灯是关闭状态。

注意到操作之后的亮着的灯的数量为 \(A+k\),此时仍有连续的灯的数量为 \(k-1\),从某个灭着的灯处看,我们发现最优情况下每隔 \(k\) 个位置就有一盏灯是灭着的,我们有 \(A+k\le n-1-\frac{n-1}{k}\)

所以 \(A\le n-k-1-\frac{n-1}{k}\)

于是答案的上界为 \(\max_{k} (n-k-1-\frac{n-1}{k})+1\),即 \(A\) 到达上界后仍然可以增大 \(1\)

考虑这样的策略,我们先固定此 \(k\),然后维护集合 \(A\) 满足 \(\{x\in A,x\ne 1\pmod k\}\)

不难证明集合中不存在连续的 \(k\) 个数(注意到 \(1\) 被 ban 掉了)

所以我们每次操作总能够选出 \(k\) 个数,同时使答案至少增大 \(1\)

可以证明当 \(k=\sqrt{n}\)(下取整)时得到最优解。

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int N = 1000 + 5 ; 
int n, R, Z, k, cnt, A[N] ;  
signed main()
{
	fflush(stdout) ; 
	n = gi(), k = sqrt(n) ;
	R = n - k - (n - 1) / k ;
	if( R <= 0 ) { puts("0") ; exit(0) ; }
	while(1) {
		printf("%d ", k ) ; cnt = 0 ;
		rep( i, 1, n ) {
			if(A[i] || i % k == 1) continue ; 
			printf("%d ", i ), A[i] = 1, ++ Z, ++ cnt ; 
			if( cnt == k ) break ; 
		}
		puts("") ; 
		int x ; cin >> x ; 
		rep( i, 0, k - 1 ) {
			if( A[(i + x - 1) % n + 1] ) -- Z ;
			A[(i + x - 1) % n + 1] = 0 ; 
		}
		if( Z == R ) break ; 
	}
	puts("0") ; 
	return 0 ;
}
posted @ 2020-10-05 21:29  Soulist  阅读(162)  评论(0编辑  收藏  举报