【NOI2019】I君的探险(交互)(随机化)(整体二分)

传送门


题解:

分段瞎搞:

1. n ≤ 500 n\leq 500 n500 直接暴力。

2.树,且不允许调用 check。
题目保证父亲小于自己,于是整体二分一下就行了。

3.一般情况+特性A。

随机选择 n / 2 n/2 n/2 个点 modify一下,然后看有多少个点被改变。

被改变意味着有奇数条边向前连,奇数>0,重复下去可以保证找到边。

期望有 n / 3 n/3 n/3 个点向前连了奇数条边。瞎搞即可。期望复杂度 O ( m log ⁡ m ) O(m\log m) O(mlogm)


代码:

#include "explore.h"

#ifndef ONLINE_JUDGE
#include "grader.cpp"
#endif

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

//modify( [0, N) )
//query ( [0, N) )
//report( [0, N), [0, N) )
//check ( [0, N) ) 

cs int N=2e5+7;

int n,m;

int op[N];
int p[N];

namespace Force{

void Main(){
	for(int re i=0;i<n-1;++i){
		modify(i);
		for(int re j=i+1;j<n;++j)
			if(op[j]!=query(j))
				report(i,j),op[j]^=1;
	}
}

}

namespace Tree{

int cur,tmp[N];

void solve(int l,int r,int ql,int qr){
	if(ql>qr)return;if(l==r){
		for(int re i=ql;i<=qr;++i)
			report(l,p[i]);return;
	}int mid=(l+r)>>1;
	while(cur<mid)modify(++cur);
	while(cur>mid)modify(cur--);
	for(int re i=ql;i<=qr;++i)
		if(p[i]<=mid||query(p[i]))
			op[p[i]]=true;
		else op[p[i]]=false;
	int L=ql,R=qr;
	for(int re i=ql;i<=qr;++i)
		tmp[op[p[i]]?L++:R--]=p[i];
	for(int re i=ql;i<=qr;++i)p[i]=tmp[i];
	solve(l,mid,ql,R);solve(mid+1,r,L,qr);
}

void Main(){
	for(int re i=0;i<n;++i)p[i]=i;
	cur=-1;solve(0,n-1,1,n-1);
}

}

namespace Normal{


bool ban[N];
std::mt19937 R(time(0));
std::vector<int> G[N];

bool need_check;
void Report(int u,int v){
	report(u,v);--m;
	G[u].push_back(v);
	G[v].push_back(u);
	if(need_check){
		ban[u]=check(u);
		ban[v]=check(v);
	}
}

int Qry(int u){
	int res=query(u);
	for(int v:G[u])
		res^=op[v];
	return res;
}

void solve(cs std::vector<int> &q,int l,int r){
	if(q.empty())return ;
	if(l==r){
		for(int v:q)if(l!=v)
			Report(p[l],p[v]);
		return;
	}int mid=(l+r)>>1;std::vector<int> ql,qr;
	for(int re i=l;i<=mid;++i)
		modify(p[i]),op[p[i]]=true;
	for(int v:q)
		if(v<=mid||Qry(p[v]))
			ql.push_back(v);
		else qr.push_back(v);
	for(int re i=l;i<=mid;++i)
		modify(p[i]),op[p[i]]=false;
	solve(ql,l,mid);solve(qr,mid+1,r);
}

void Main(){
	need_check=m>=n-1;
	for(int re i=1;i<n;++i)p[i]=i;
	while(m){
		int nn=n;n=0;
		for(int re i=0;i<nn;++i)
			if(!ban[p[i]])p[n++]=p[i];
		std::shuffle(p,p+n,R);
		std::vector<int> q;
		for(int i=0;i<n;++i)
			q.push_back(i);
		solve(q,0,n-1);
	}
}

}

void explore(int _n,int _m){
	n=_n,m=_m;
	if(n<=500)
		Force::Main();
	else if(n%10==7)
		Tree::Main();
	else Normal::Main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOI普及组考试的难度适中。 首先,NOI普及组考试的题目设计会相对简单一些,以便初学者能够更好地理解和解答。相比于NOI提高组考试的更高难度,普及组考试更注重基础知识和算法的应用。这样不仅使得考生能够更容易掌握考试内容,也能够更好地巩固和拓展自己的计算机科学基础。 其次,考试题目会涵盖计算机科学的各个方面,包括数据结构、算法、编程和理论等。这样可以让考生对计算机科学的不同领域都有所了解,并培养他们的综合能力和解决问题的能力。 此外,NOI普及组考试对编程语言的要求相对宽松。考生可以使用自己熟悉的编程语言进行解题。这样不仅给予考生更多选择的权利,也能更好地发挥他们的编程能力。 然而,虽然NOI普及组考试的难度相对较低,但仍然需要考生具备一定的计算机知识和编程基础。要取得好的成绩,考生需要对常用的数据结构和算法有一定的了解和掌握,并且能够熟练地进行编程实现。同时,考生还需要培养自己的逻辑思维和问题解决能力,以便能够高效地解答问题。 综上所述,NOI普及组考试的难度适中,既考查了考生的计算机科学基础知识和编程能力,又给予了初学者足够的实践机会和提升空间。这样不仅为考生提供了学习和发展的平台,也能够激发他们对计算机科学的兴趣和热情。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值