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 ;
}
标签:盏灯,le,cc,int,CF1368F,操作,flus 来源: https://www.cnblogs.com/Soulist/p/13771687.html