交互题训练2

这篇博客分享了两道交互类型的竞赛题目,分别是IOI 2018的组合动作和WC 2018的即时战略。在[IOI 2018]的题目中,通过n+2次询问确定了一个由四种字符组成的字符串。在[WC 2018]的即时战略题目中,推荐使用LCT(Light Cone Traversal)解决动态点分治问题,实现O(nlog2n)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

做了两道交互题,一道是[IOI 2018]的组合动作,一道是[WC 2018]的即时战略。
[IOI 2018]组合动作
题意:有一个字符串,长度为n,每次你可以询问一个长度不超过4n的字符串,会返回给你又是这个字符串的子串又是原先字符串的前缀的字符串的最长长度。试在n+2次询问内确定字符串。
数据保证该字符串由4种字符(A,B,X,Y)组成,且保证开头字符只出现一次。
我们考虑逐位确定。
花两步确定第一位。令c1,c2,c3为剩下的3个字符。
如果确定了前k位,如何确定第n位。
k = n − 1 k=n-1 k=n1,花2次确定即可。
k &lt; n − 1 k&lt;n-1 k<n1,前k位字符串为str,询问str-c1-c1-str-c1-c2-str-c1-c3-str-c2
如果答案为k,则该位字符为c3,k+1为c2,k+2为c1
询问次数即n+2
太妙了!

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#include"combo.h"
using namespace std;
string Ans,ask;
string guess_sequence(int n){
	char fi;
	char c[4];int cnt=0;
	ask.push_back('A');
	ask.push_back('B');
	if(press(ask)){
		ask.clear();
		ask.push_back('A');
		if(press(ask)){
			Ans.push_back('A');
			fi='A';
		}else{
			Ans.push_back('B');
			fi='B';
		}
	}else{
		ask.clear();
		ask.push_back('X');
		if(press(ask)){
			Ans.push_back('X');
			fi='X';
		}else{
			Ans.push_back('Y');
			fi='Y';
		}
	}
	if('A'!=fi)c[++cnt]='A';
	if('B'!=fi)c[++cnt]='B';
	if('X'!=fi)c[++cnt]='X';
	if('Y'!=fi)c[++cnt]='Y';
	for(register int i=2;i<n;++i){
		ask.clear();
		for(register int j=0;j<i-1;++j)ask.push_back(Ans[j]);
		ask.push_back(c[1]);
		for(register int j=0;j<i-1;++j)ask.push_back(Ans[j]);
		ask.push_back(c[2]);ask.push_back(c[1]);
		for(register int j=0;j<i-1;++j)ask.push_back(Ans[j]);
		ask.push_back(c[2]);ask.push_back(c[2]);
		for(register int j=0;j<i-1;++j)ask.push_back(Ans[j]);
		ask.push_back(c[2]);ask.push_back(c[3]);
		int res=press(ask);
		if(res==i-1)Ans.push_back(c[3]);
		else if(res==i)Ans.push_back(c[1]);
		else Ans.push_back(c[2]);
	}
	if(n>1){
		ask.clear();
		for(register int j=0;j<n-1;++j)ask.push_back(Ans[j]);
		ask.push_back(c[1]);
		for(register int j=0;j<n-1;++j)ask.push_back(Ans[j]);
		ask.push_back(c[2]);
		int res=press(ask);
    	if(res==n-1)Ans.push_back(c[3]);
		else{
			ask.clear();
			for(register int j=0;j<n-1;++j)ask.push_back(Ans[j]);
			ask.push_back(c[1]);
			if(press(ask)==n)Ans.push_back(c[1]);
			else Ans.push_back(c[2]);
		}
	}
	return Ans;
}

[WC 2018]即时战略
为什么要写重构的动态点分治呢?又臭又长?还可能会被卡常
我推荐使用LCT
题意…不说了
如果我们当前想从1跳到x。
我们可以考虑使用LCT。
沿着一条条重链往下跳,即爬重链。
在重链上跳,play的值会如下所示:
儿子儿子儿子…儿子非重链上的儿子或爸爸爸…爸爸爸爸爸爸
在重链对应的Splay上进行二分操作即可。
到x时access一下即可。
询问次数&&时间复杂度 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include"rts.h"
using namespace std;
#define Maxn 300010
bool vis[Maxn];
int fa[Maxn],ch[Maxn][2],le[Maxn],ri[Maxn];
int seq[Maxn];

bool who(int k){return ch[fa[k]][1]==k;}
bool isrt(int k){return ch[fa[k]][0]!=k&&ch[fa[k]][1]!=k;}
void push_up(int k){
	le[k]=ri[k]=k;
	if(ch[k][0])le[k]=le[ch[k][0]];
	if(ch[k][1])ri[k]=ri[ch[k][1]];
}
void rot(int k){
	int dir=who(k),f=fa[k];
	if(!isrt(f))ch[fa[f]][who(f)]=k;
	fa[k]=fa[f];
	ch[f][dir]=ch[k][dir^1];fa[ch[k][dir^1]]=f;
	ch[k][dir^1]=f;fa[f]=k;
	push_up(f);
	push_up(k);
}

void Splay(int u){
	for(int f=fa[u];!isrt(u);rot(u))
		if(!isrt(f=fa[u]))rot(who(f)==who(u)?f:u);
}

void access(int u){
	for(int t=0;u;t=u,u=fa[u]){
		Splay(u);
		ch[u][1]=t;
		push_up(u);
	}
}

int findroot(int k){
	while(!isrt(k))k=fa[k];
	return k;
}

void play(int n,int T,int datatype){
	srand(time(NULL));
	vis[1]=true;
	if(datatype==3){
		int node1=1,node2=1;
		int d=1;
		while(d<n){
			z1:int x=1ll*rand()*rand()%n+1;
			if(vis[x])goto z1;
			int to=explore(node1,x);int tp=0;
			if(vis[to])to=explore(node2,x),tp=1;
			int &side=tp?node2:node1;
			vis[to]=true;
			d++;
			side=to;
			while(!vis[x]){
				side=explore(side,x);
				vis[side]=true;
				d++;
			}
		}
	}
	for(register int i=1;i<n;++i)seq[i]=i+1;
	random_shuffle(seq+1,seq+n);
	for(register int i=1;i<n;++i)
		if(!vis[seq[i]]){
		int node=findroot(1),to;
		solve:
		while(!vis[seq[i]]){
			Splay(node);
			while(true){
				to=explore(node,seq[i]);
				if(to==ri[ch[node][0]])node=ch[node][0];
				else if(to==le[ch[node][1]])node=ch[node][1];
				else{
					if(!vis[to]){
						vis[to]=true;
						fa[to]=node;
					}
					node=to;
					node=findroot(node);
					goto solve;
				}
			}
		}
		access(seq[i]);
	}
	return;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值