非传统题训练

Codechef Guessing Game

x ≤ 1 e 9 x \leq 1e9 x1e9,你可以问 y y y < x , > x <x , >x <x,>x还是 = x =x =x,但是他可以对于 < < < > > >撒谎(说成另一种),但是不能连续两次撒谎,询问次数 ≤ 120 \leq 120 120

连续询问两次,每次询问可以得到两个区间,一个区间为撒谎时的区间,另一个为不撒谎时的区间。
这两个撒谎时的区间的交区间就可以排除掉。
那么我们第一次询问问区间 n 2 \frac n2 2n的位置,第二次问第一次撒谎区间的中点,则我们可以一次排除掉 n 4 \frac n4 4n的区间,询问次数为 2 log ⁡ 4 3 1 e 9 = 140 2\log_{\frac 43} 1e9 = 140 2log341e9=140,卡不进去。
发现每两次询问,第二次询问的撒谎区间如果包含了第一次选的点,那么会有 n 2 \frac n2 2n大小的仍然合法的区间在第二次的撒谎区间内,在这 n 2 \frac n2 2n的区间内选中点作为第三次询问,像之前一样做又可以排除掉 n 4 \frac n4 4n的区间。(其实第三次也可以继续再做下去这样就是官方正解了,但是我求第三次之后没有继续也能过。)
那么这样可以三次询问排除掉 n 2 \frac n2 2n的区间,相当于一次询问能排除掉 0.20628113542798557582164679862673 n 0.20628113542798557582164679862673n 0.20628113542798557582164679862673n的区间。
但是如果第二次询问的撒谎区间不包含了第一次选的点,则还是只能一次排除掉 0.25 n 0.25n 0.25n的区间,一次询问是能排除 0.13397459621556135323627682924706 n 0.13397459621556135323627682924706n 0.13397459621556135323627682924706n的区间。
平摊一下复杂度。
假设我们第二个询问的点定在 n k \frac nk kn处,则第一种会剩下的是 ( n 4 + n k ) 1 3 (\frac n4 + \frac nk)^{\frac 13} (4n+kn)31的,
第二种是 ( n − n k ) 1 2 (n - \frac nk)^{\frac 12} (nkn)21的。
反正取 k = 5 3 k = \frac 53 k=35是可以过的。
A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

set<pii>st;
int n;

int qry(int x){
	printf("%d\n",x);
	fflush(stdout);
	static char s[3];
	scanf("%s",s);
	if(s[0] == 'E') exit(0);
	if(s[0] == 'L') return 0;
	return 1;
}

int find(int x){
	for(pii u:st){
		if(u.second - u.first + 1 >= x)
			return u.first + x - 1;
		x -= u.second - u.first + 1;
	}
	assert(0);
	return -1;
}

set<pii>::iterator split(int l){
	set<pii>::iterator it = st.upper_bound(mp(l,0x3f3f3f3f));
	it--;
	if((*it).first == l) return it;
	int L = (*it).first , R = (*it).second;
	st.erase(it);
	st.insert(mp(L,l-1));
	return st.insert(mp(l,R)).first;
}

void erase(int l,int r){
	int t = r - l + 1;
	l = find(l) , r = find(r);
	set<pii>::iterator itr = split(r+1) , itl = split(l);
	for(;itl != itr;) st.erase(itl++);
	n -= t;
}

int main(){
	scanf("%d",&n);
	st.insert(mp(1,n));
	for(;n >= 3;){
		int m = (1+n) >> 1;
		int a = find(m);
		int ta = qry(a);
		if(ta == 1){
			int m1 = max(1,(1 + m-1) * 3 / 5);
			int b = find(m1);
			int tb = qry(b);
			if(tb == 1){
				erase(m,m);
				erase(1,m1);
			}
			else{
				int m2 = (m+1+n) >> 1;
				int c = find(m2);
				int tc = qry(c);
				if(tc){
					erase(m1,m2);
				}
				else{
					erase(m2,n);
					erase(m1,m);
				}
			}
		}
		else{
			int m1 = min(n,m+1+(n-m)*2/5);
			int b = find(m1);
			int tb = qry(b);
			if(tb == 0){
				erase(m1,n);
				erase(m,m);
			}
			else{
				int m2 = (1+m-1) >> 1;
				int c = find(m2);
				int tc = qry(c);
				if(tc == 0){
					erase(m2,m1);
				}
				else{
					erase(m,m1);
					erase(1,m2);
				}
			}
		}
	}
	rep(i,1,n) qry(find(i));
}

CF1354G Find a Gift

用随机化求出第一个是否是礼品。
如果是石头的话就可以直接倍增求答案了。
A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 1005
using namespace std;

int n,K;
int Ran(int a,int b){ return rand() % (b-a+1) + a; }

int ask(int a,int b,int c,int d){
	printf("? %d %d\n",b-a+1,d-c+1);
	for(int i=a;i<=b;i++) printf("%d%c",i," \n"[i==b]);
	for(int i=c;i<=d;i++) printf("%d%c",i," \n"[i==d]);
	fflush(stdout);
	char s[100];
	scanf("%s",s);
	if(s[0] == 'F') return 1;
	if(s[0] == 'S') return -1;
	if(s[0] == 'E') return 0;
	exit(1);
}

int main(){
	int T;
	for(scanf("%d",&T);T--;){
		scanf("%d%d",&n,&K);
		bool fg = 0;
		for(int i=1;i<=29;i++){
			int t = Ran(2,n);
			if(ask(1,1,t,t) < 0){			
				fg = 1;
				break;
			}
		}
		if(fg){
			printf("! %d\n",1);
			fflush(stdout);
			continue;
		}
		for(int i=1,s=1;s<=n;i++,s<<=1){
			if(s*2 > n || ask(1,s,s+1,s<<1) != 0){
				int t = s >> 1;
				for(i--;i>=1;i--){
					if(s+t <= n && ask(1,t,s+1,s+t) == 0) s += t;
					t >>= 1;
				}
				printf("! %d\n",s+1);
				fflush(stdout);
				break;
			}
		}
	}
}

CodeChef Guess the Prime!

标算被吊着打
x = 2 15 x = 2^{15} x=215 t = x 2   m o d   P t = x^2 \bmod P t=x2modP, k = x 2 − t k = x^2 - t k=x2t
则若 t = 0 t = 0 t=0 , 则 P = 2 P = 2 P=2
否则 P P P为奇数,可以把 k k k用一个奇数 r r r表示成 ( k = r × 2 a )   m o d   P = 0 (k = r \times 2^a) \bmod P = 0 (k=r×2a)modP=0
也就是 r   m o d   P = 0 r \bmod P = 0 rmodP=0
所以 r + 1 2   m o d   P = 1 2 \frac {r+1}2 \bmod P = \frac 12 2r+1modP=21,也就是我们找到了   m o d   P \bmod P modP意义下 2 2 2的逆元(注意这个逆元不唯一,所以无法直接推出 P P P)。
那么我们求出 y = x 2   m o d   P y = x^2 \bmod P y=x2modP,这个 y y y就是在   m o d   P \bmod P modP意义下唯一的 4 4 4的逆元。
对于 P = 4 k + 1 P = 4k+1 P=4k+1型质数, y = 3 P + 1 4 y = \frac {3P+1}4 y=43P+1 P = 4 y − 1 3 P = \frac {4y-1}3 P=34y1
对于 P = 4 k + 3 P = 4k+3 P=4k+3型质数, y = P + 1 4 y = \frac {P+1}4 y=4P+1 P = 4 y − 1 P = 4y-1 P=4y1
这两个不能同时满足,讨论一下即可。

A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define LL long long
using namespace std;

int ask(int a){
	printf("1 %d\n",a);
	fflush(stdout);
	int r;scanf("%d",&r);
	return r;
}

void ans(int a){
	printf("2 %d\n",a);
	fflush(stdout);
	char s[10];
	scanf("%s",s);
}

int main(){
	int T;
	for(scanf("%d",&T);T--;){
		LL x = ask(1<<15);
		if(!x) ans(2);
		else{
			for(x=(1<<30)-x;!(x&1);x>>=1);
			LL y = ask((x+1)/2);
			if((4 * y - 1) % 3 == 0 && y != 1) ans((4 * y - 1) / 3);
			else ans(4 * y - 1);
		}
	}
}

CF 1372F. Omkar and Modes

S o l v e ( l , r ) : Solve(l,r): Solve(l,r):
1.查询 [ l , r ] [l,r] [l,r]中的众数 x x x,出现次数 f f f
2.找到 x x x [ l , r ] [l,r] [l,r]中的一个出现位置 p p p
3.若 [ p − f + 1 , p ] [p-f+1,p] [pf+1,p]的众数为 x x x,则我们可以得到连续的 x x x的左端点,进而得到右端点,否则 [ p , p + f − 1 ] [p,p+f-1] [p,p+f1]的众数一定为 x x x,同理得到。
4.将 x x x出现的位置答案数组赋值为 x x x,设 x x x的区间为 [ q l , q r ] [ql,qr] [ql,qr],递归解决 S o l v e ( l , q l − 1 ) , S o l v e ( q r + 1 , r ) Solve(l,ql-1),Solve(qr+1,r) Solve(l,ql1),Solve(qr+1,r)

上面这个过程中除开 2 2 2以外的操作合计次数是 3 k 3k 3k,意思是我们要用一次求出 x x x [ l , r ] [l,r] [l,r]中的一个出现位置。
首先如果 a a a处不是 x x x,那么如果我们增加 a a a继续判断,可以一次增加 f f f,因为 x x x出现一定是长度为 f f f的一段。
其次,因为在 [ l , r ] [l,r] [l,r] x x x的长度最长,也就是说我们增加 f f f之后的位置的值不会和之前查询到位置在之前的任意一个值相同。
m a p map map维护一下。
显然如果当前值已经出现过,我们直接从 m a p map map取,
否则我们 + = f +=f +=f后的值之前都不会出现过(这里就需要我们从已知的值中最大的比 x x x小的值的右边开始枚举才能保证),所以均摊下来是 k k k次。
A C   C o d e \mathcal AC \ Code AC Code

#include<bits/stdc++.h>
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
using namespace std;

int n,ans[maxn];
map<int,int>mAp;

pair<int,int>ask(int l,int r){
	l = max(l , 1) , r = min(r , n);
	r = max(r , l);
	printf("? %d %d\n",l,r);
	fflush(stdout);
	pair<int,int>R;
	scanf("%d%d",&R.first,&R.second);
	return R;
}

void Solve(int l,int r){
	if(l > r) return;
	auto u = ask(l,r);
	auto it = mAp.upper_bound(u.first);
	int x;int f,b;
	if(it == mAp.begin()) x = l;
	else if((*--it).first == u.first){
		int p = (*it).second;
		auto v = ask(p-u.second+1,p);
		if(v.first == u.first){
			f = p - v.second + 1,
			b = f + u.second - 1;
		}
		else{
			v = ask(p,p+u.second-1);
			b = p + v.second - 1,
			f = b - u.second + 1;
		}
		rep(i,f,b) ans[i] = u.first;
		Solve(l,f-1) , Solve(b+1,r); 
		return;
	}
	else x = max(l , (*it).second + u.second);
	int p;
	for(;;x += u.second){
		auto t = ask(x,x);
		mAp[t.first] = x;
		if(t.first == u.first){
			p = x;
			break;
		}
	}
	
	auto v = ask(p-u.second+1,p);
	if(v.first == u.first){
		f = p - v.second + 1,
		b = f + u.second - 1;
	}
	else{
		v = ask(p,p+u.second-1);
		b = p + v.second - 1,
		f = b - u.second + 1;
	}
	rep(i,f,b) ans[i] = u.first;
	Solve(l,f-1) , Solve(b+1,r); 
}

int main(){
	scanf("%d",&n);
	Solve(1,n);
	printf("!");
	rep(i,1,n) printf(" %d",ans[i]);
	fflush(stdout);
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值