cf1697 D. Guess The String

题意:

交互题。

猜一个小写字符串 s,两种询问方式:

  • 1 i ,回答 si 是什么字符
  • 2 l r,回答 slsr 中的不同字符数量

|s|1000, 两种询问的次数限制分别为 26,6000

思路:

第一种询问不能超过 26 次,那肯定是先确定每种字符的分布(但不知道具体是什么字符),再询问具体是啥字符

首先可以用 n 次第二种询问得到每种字符第一次出现的位置。方法是询问 2 1 2, 2 1 3, 2 1 4 直到回答即不同字符数增大1,说明找到新段的起点。

岔路:对某 si 如何找到它右边最近的相同字符,即最小的 k>i, s.t. si=sk

发现对于两种询问 2 i j2 i+1 j-1 ,若 j<k 则前者的回答比后者大2,若 j=k 则大1,若 j>k 则相同。这样可以二分,用 2log2n 次确定一段,确定所有段就要 n2log2n 次,太多了

正确的复杂度很可能形如 n(),这个 n 应该是去不掉的。那就需要把每次找相同字符的询问次数优化到 (6000n)/n=5 次左右,而 log226 刚好就是 5

si 待确定,它左边的所有位置都已确定。把 si 左边出现过的每种字符 ch 按最后出现位置 las(ch) 排序。然后询问 2 las(ch) i ,若回答为 [las(ch),i1] 中的种类数 cntch,就说明 si 就是 [las(ch),i1] 中的某种字符;若回答是 cnt+1,就说明 si 不在其中,得往 las 值更小的字符找。

这个 cntch 就是排序后 las 数组中 ch 到末尾的元素数

注意上面搞了这么多我们都不知道每段具体是什么字符,用一下第一种询问就好了。实际写码不必先问完第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;
}
posted @   Bellala  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示