AGC044D Guess the Password

题目传送门

分析:
读题目所给的距离的定义能够明白
设两字符串\(A,B\)的距离为\(dis(A,B)\),最长公共子序列为\(f(A,B)\),那么:
\(dis(A,B)+f(A,B)=max(|A|,|B|)\)
这样我们能够用他的距离函数进行如下操作:
1、查询某一种字符的出现次数,可以构造全为某种字符\(c\)的长度为128的字符串\(C\)\(c\)的出现次数即为\(128-dis(S,C)\)
2、查询一个长度不大于\(S\)的串\(C\)是否为\(S\)的子序列,只需判断\(f(S,C)==|C|\),即\(|S|-dis(S,C)==|C|\)
\(Q\)大概是\(O(LlogL)\)级别的,考虑分治算法
先把每一种字符的出现次数求出来,之后启发式合并两种字符集\({A},{B}\),合并直接暴力判断插入某一种字符是否合法即可,一次合并查询次数为\(|A|+|B|\)
由于启发式合并,询问次数是\(O(LlogL)\)级别的
交互题必被开除人籍系列

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<string>

#define maxn 1000005
#define INF 0x3f3f3f3f

using namespace std;

inline int getint()
{
	int num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int N=128,C=62,n;
vector<string>S;

inline bool cmp(string x,string y)
{return x.size()<y.size();}
inline char id(int x)
{
	if(x<26)return x+65;
	if(x<52)return x+71;
	return x-4;
}
inline int query(string x)
{
	cout<<"? "<<x<<endl;
	return getint();
}
inline bool check(string x)
{return query(x)+x.size()==n;}

int main()
{
	for(int i=0;i<C;i++)
	{
		string x(N,id(i));
		int tmp=N-query(x);
		n+=tmp;if(tmp)S.push_back(string(tmp,id(i)));
	}
	while(S.size()>1)
	{
		sort(S.begin(),S.end(),cmp);
		string a=S[0],b=S[1],res;
		S.erase(S.begin()),S.erase(S.begin());
		while(a.size()&&b.size())
			if(check(res+a[0]+b))res+=a[0],a.erase(a.begin());
			else res+=b[0],b.erase(b.begin());
		res+=a,res+=b;
		S.push_back(res);
	}
	cout<<"! "<<S[0]<<endl;
}

posted @ 2020-07-17 17:05  Izayoi_Doyo  阅读(266)  评论(0编辑  收藏  举报