CF1368F
这是一道交互题。
Alice 和 Bob 在玩游戏,有 (n) 盏灯排成一个圈,顺时针标号为 (1sim n),初始所有灯都是关着的。
每轮操作为:
- Alice 选择一个 (kin [1,n]) 然后打开任意 (k) 盏灯(可以重复打开)
- Bob 关闭一段长度为 (k) 的区间的灯。
设 (R) 表示经过若干轮后开着的灯的数量的最大值。
你需要在不超过 (10^4) 次操作达到这个上界。
(nle 1000)
- 请注意,灯是环形摆放。
Solution
设 (A) 表示当前亮着的灯的数量。
假设当前增加了 (k) 盏灯,里面如果 Bob 无法消去 (k) 盏灯,那么 (A) 将会增大。
换而言之,如果操作后不存在连续的 (k) 盏灯,那么当前操作就可以使得 (A) 增大。
考虑 (A) 能够增大的必要条件是什么,即存在一个 (k) 使得操作后有不存在连续的 (k) 盏灯。
注意到 (R<n),所以我们至少有一盏灯是关闭状态。
注意到操作之后的亮着的灯的数量为 (A+k),此时仍有连续的灯的数量为 (k-1),从某个灭着的灯处看,我们发现最优情况下每隔 (k) 个位置就有一盏灯是灭着的,我们有 (A+kle n-1-frac{n-1}{k})
所以 (Ale n-k-1-frac{n-1}{k})
于是答案的上界为 (max_{k} (n-k-1-frac{n-1}{k})+1),即 (A) 到达上界后仍然可以增大 (1)
考虑这样的策略,我们先固定此 (k),然后维护集合 (A) 满足 ({xin A,x e 1pmod 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 ;
}