专栏文章

题解:CF2108D Needle in a Numstack

CF2108D题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min6hi65
此快照首次捕获于
2025/12/01 21:21
3 个月前
此快照最后确认于
2025/12/01 21:21
3 个月前
查看原文
中考前最后一场 CF(指的是我回归文化课以前的最后一场)的好题。
拖得也有个半年了。

首先不难发现 aabb 肯定都是有循环的。设循环节为 kka1a_1 一定等于 ak+1a_{k+1},因为 a[2,k]a_{[2,k]} 都相等且 a[1,k]a_{[1,k]}a[2,k+1]a_{[2,k+1]} 在重排后意义相同。
首先询问 aa 的前 kk 个数和 bb 的后 kk 个数,找到 aabb 分别的循环节。如果两个循环节可以直接拼在一起,说明是可以任意分割出 aabb 的,只要 a,bk|a|,|b|\ge k 即可。该部分询问有 2k2k 次。
考虑二分得到 a,ba,b 的大概分界点。直接选一个可以使序列有多种形态的位置(指的是 a,ba,b 拼起来后模 kk 同余且值不同的位置)。二分出这里的位置 xxa,ba,b 的分界一定在 [xk,x+k][x-k,x+k] 之间。对有多种形态的位置进行询问,得到可能的分界点。
总询问次数 4k+logn4k+\log n 次。被题解区神仙吊打。
CPP
#include<bits/stdc++.h>
#define int long long
#define mp(a,b) make_pair(a,b)
using namespace std;

inline int read(){
	int a=0,b=1;
	char c=getchar();
	while(!isdigit(c)){
		if(c=='-')	b=-1;
		c=getchar();
	}
	while(isdigit(c)){
		a=(a<<1)+(a<<3)+(c-'0');
		c=getchar(); 
	}
	return a*b;
} 
inline void write(int x){
	if(x<0)	putchar('-'),x=-x;
	if(x>=10)	write(x/10);
	putchar(x%10+48);
}
inline void write1(int x){
	write(x),putchar(' ');
}
inline void write2(int x){
	write(x),putchar('\n');
}

//似乎是一个 4k+log n  的做法,记录一下 

const int N=460;
const int M=1919810;
int query[N];
int query1[N];

int ans[M];

//int sol[N];	//代表哪些位置是我们可以去处理的  
int sol_first;
int sol_lst;	//代表与 sol_first 相对的值  
int n,k; 
int idx;

int sum_;	//	节数  
void solve(){
	idx=0;
	sol_first=1e18;
	n=read(),k=read();
	sum_=n/k +(n%k!=0);
	if(n==k*2){
		putchar('!'),putchar(' '),write1(k),write2(k);
		fflush(stdout); 
		return;
	}
	for(int i=n-k+1;i<=n;i++){
		//现在查询的 
		putchar('?'),putchar(' '),write2(i);
		fflush(stdout); 
		query[++idx]=i;
		query1[idx]=read();
		ans[i]=query1[idx];
		int j=i%k;
		if(j==0)	j=k;
		putchar('?'),putchar(' '),write2(j);
		fflush(stdout);
		query[++idx]=j;
		query1[idx]=read();
		ans[j]=query1[idx];
		if(ans[i]!=ans[j]){
			if(sol_first>j){
				sol_first=j;
				sol_lst=i;
			} 
		}
	}
	if(sol_first>1e16){
		putchar('!'),putchar(' '),write2(-1);
		fflush(stdout);
		return;//无法确定  
	}
	int l=1,r=(sol_lst-sol_first)/k+1;
	int mid=l+r>>1;
	while(l<r){
		int now=(mid-1)*k+sol_first;	//代表目前的位置 
		putchar('?'),putchar(' '),write2(now);
		fflush(stdout);
		query[++idx]=now;
		query1[idx]=read();
		ans[now]=query1[idx];
		if(ans[now]==ans[sol_first])	l=mid+1;
		else r=mid;
		mid=l+r>>1;
	}
//	cout<<'#'<<mid<<endl;
	//现在用了 2k+log 次询问  
	mid=(mid-1)*k+sol_first;	//代表目前所在的位置 
	
	int max_left=k,min_right=n-k+1; //如果 max_left 和 min_right 之间存在值 就为 -1  
	for(int i=min(mid+k,n-k);i>=max(k,mid-k);i--){
		//判断所有的位置和前后是否相关 
		int l=i%k;
		int r=l+sum_*k;
		if(l==0)	l=k;
		while(r>n)	r-=k;
		if(ans[l]==ans[r])	continue;	//说明这里进行询问没有意义  
		putchar('?'),putchar(' '),write2(i);
		fflush(stdout);
		query[++idx]=i;
		query1[idx]=read();
		ans[i]=query1[idx];
		if(ans[i]==ans[l]){
			//代表这里是左翼的 
			max_left=max(max_left,i); 
		}
		else{
			//右翼 
			min_right=min(min_right,i); 
		}
	} 
	if(max_left==min_right-1){
		putchar('!'),putchar(' '),write1(max_left),write2(n-max_left);
		fflush(stdout);
	}
	else{
		putchar('!'),putchar(' '),write2(-1);
		fflush(stdout);
	}
	for(int i=1;i<=idx;i++){
		ans[query[i]]=0;
		query[i]=0;
		query1[i]=0;
	}
	idx=0;
}
#undef int
int main(){
	#define int long long
	int T=read();
	while(T--)	solve();
	putchar('\n');
	return 0;
} //一定要挽回罗琪钧的遗憾啊,一定会挽回罗琪钧的遗憾的! 

评论

0 条评论,欢迎与作者交流。

正在加载评论...