做了两道交互题,一道是[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=n−1,花2次确定即可。
若
k
<
n
−
1
k<n-1
k<n−1,前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;
}