cf1697 D. Guess The String
题意:
交互题。
猜一个小写字符串 ,两种询问方式:
1 i
,回答 是什么字符2 l r
,回答 中的不同字符数量
两种询问的次数限制分别为
思路:
第一种询问不能超过 26 次,那肯定是先确定每种字符的分布(但不知道具体是什么字符),再询问具体是啥字符
首先可以用 次第二种询问得到每种字符第一次出现的位置。方法是询问 2 1 2, 2 1 3, 2 1 4
直到回答即不同字符数增大1,说明找到新段的起点。
岔路:对某 如何找到它右边最近的相同字符,即最小的 ?
发现对于两种询问
2 i j
和2 i+1 j-1
,若 则前者的回答比后者大2,若 则大1,若 则相同。这样可以二分,用 次确定一段,确定所有段就要 次,太多了
正确的复杂度很可能形如 ,这个 应该是去不掉的。那就需要把每次找相同字符的询问次数优化到 次左右,而 刚好就是 !
设 待确定,它左边的所有位置都已确定。把 左边出现过的每种字符 按最后出现位置 排序。然后询问 2 las(ch) i
,若回答为 中的种类数 ,就说明 就是 中的某种字符;若回答是 ,就说明 不在其中,得往 值更小的字符找。
这个 就是排序后 数组中 到末尾的元素数
注意上面搞了这么多我们都不知道每段具体是什么字符,用一下第一种询问就好了。实际写码不必先问完第2种最后才问所有第1种,怎么方便怎么写
良心 cf 交互题的询问次数限制总是贴着正确解法的复杂度
char ask1(int i) {
cout << "? 1 " << i << endl;
char c; cin >> c; return c;
}
int ask2(int l, int r) {
cout << "? 2 " << l << ' ' << r << endl;
int v; cin >> v; return v;
}
int n; string ans;
vector<pair<int,char>> las; //{最后出现位置,字符}
int findSame(int i) {
sort(all(las)); int len = las.size();
int l = 0, r = len - 1; //在las里二分
while(l < r) {
int mid = l + r + 1 >> 1;
if(ask2(las[mid].fi, i) == len-mid) l = mid;
else r = mid - 1;
}
return l;
}
void sol() {
cin >> n;
ans.pb(ask1(1)), las.pb({1,ans[0]}); //先弄一下第一个
for(int i = 2; i <= n; i++)
if(ask2(1, i) > las.size()) //全新的字符
ans.pb(ask1(i)), las.pb({i, ans.back()});
else {
int j = findSame(i); //s[j]=s[i]
ans.pb(las[j].se), las[j].fi = i;
}
cout << "! " << ans << endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?