首页 > 其他分享 >CF1854D 题解

CF1854D 题解

时间:2023-08-16 15:23:46浏览次数:59  
标签:环上 int 题解 询问 mid CF1854D nodes op

CF1854D Michael and Hotel 题解

洛谷

Codeforces

Description

这是一个交互题。

有一个有 \(n\) 个点的内向基环树森林,zlsim 位于 \(1\) 号节点,请你通过以下操作求出哪些节点(包括 \(1\))可以通过从这两点开始沿边行走若干步汇至一点。

  • 给出两个参数 \(u,k\) 和点集 \(S\),询问是否能够通过从 \(u\) 出发走 \(k\) 步达到任意 \(S\) 中的节点。

你最多可以询问 \(2000\) 次。

Solution

一个很显然的题意转化是我们要找到的是节点 \(1\) 所在的连通块。这个连通块一定是一颗内向基环树,因此我们可以很容易的找到一个环上的点。具体的方法如下:

  • 将剩余点分等为两部分。

  • 询问从 \(1\) 出发,行进 \(n\) 次之后是否能到达第一部分,若能到达,保留第一部分,若不能,保留第二部分。

  • 重复上述步骤,只剩一个点时停止。

每次减半,因此我们只需要 $ \left \lceil \log_{2}500 \right \rceil = 9$ 次操作就能找到一个点。

接下来我们不断按照上面的方式询问从上一个找到的在环上的点开始行进一次能到达的点,就能找到所有环上的点。假设环上有 \(k\) 个点,我们需要 \(9\times k\) 次操作,这样不能满足题目条件。

考虑如何加快找点的速度,我们可以进行以下操作:

  • 设已经找到的在连通块内的点集为 \(S\),剩余点集为 \(V\)。

  • \(\forall i \in V\),询问从 \(i\) 开始行进 \(|S|\) 步能否到达 \(S\),若能,将其加入 \(S\)。

  • 如果这次新找到的点数量小于 \(|S|\),结束查找。

这样我们就能找到环上的点,但也会找到一些在该连通块内不属于环上的点,这对我们接下来的操作没有影响。

现在环内所有点已经确定,我们可以用 \(n - |S|\) 次询问确定剩余的点,具体的,对于每个不在 \(S\) 中的点 \(u\),询问从 \(u\) 开始行进 \(n\) 步能否到达 \(S\),若能到达一定在该连通块内,否则不在该连通块内。

假设我们通过二分找到 \(p\) 个环上的点,需要的询问数如下:

  1. 二分:\(9 \times p\)。

  2. 倍增找点:令\(p_{1} = p, \forall i > 1,p_{i} = 2 p_{i - 1}\),则第 \(i\) 次需要 \(n - p_{i}\) 个询问,当 \(p_{i} \ge n\) 时结束(假设这时 \(i = x\))。

  3. 最后需要 \(n - p_{x}\) 次询问确定剩余点。

当 \(p\) 取 \(63\) 时满足题意。

Codes

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define max_n 511

int n;
int tot = 0;
int ls = 1;
bool check(int tim,int l,int r)
{
    cout<<"? "<<ls<<" "<<tim<<" "<<r - l + 1<<" ";
    for(int i = l;i <= r;i++)
    {
        cout<<i<<" ";
    }
    cout<<endl;
    int op;
    cin>>op;
    return op;
}
set<int> nodes;
bool vis[max_n];
signed main()
{
    ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
    cin>>n;
    int l = 1,r = n;
    ls = 1;
    while(l < r)
    {
        int mid = (l + r) >> 1;
        if(check(1000,l,mid))
        {
            r = mid;
        }
        else
        {
            l = mid + 1;
        }
    }
    nodes.insert(l);
    vis[l] = 1;
    ls = l;
    for(int i = 1;i <= 62;i++)
    {
        l = 1,r = n;
        while(l < r)
        {
            int mid = (l + r) >> 1;
            if(check(1,l,mid))
            {
                r = mid;
            }
            else
            {
                l = mid + 1;
            }
        }
        if(vis[l])
        {
            break;
        }
        vis[l] = 1;
        nodes.insert(l);
        ls = l;
    }    
    if(nodes.size() == 63)
    {
        int sz = nodes.size(); 
        while(true)
        {
            tot = 0;
            for(int i = 1;i <= n;i++)
            {
                if(!vis[i])
                {
                    cout<<"? "<<i<<" "<<sz<<" "<<nodes.size();
                    for(auto node:nodes)
                    {
                        cout<<" "<<node;
                    }
                    cout<<endl;
                    int op;
                    cin>>op;
                    if(op)
                    {
                        vis[i] = 1;
                        nodes.insert(i);
                    }
                }
            }
            sz *= 2;
            if(sz > nodes.size())
            {
                break;
            }
        }
    }
    
    for(int i = 1;i <= n;i++)
    {
        if(!vis[i])
        {
            cout<<"? "<<i<<" "<<1110<<" "<<nodes.size();
            for(auto node:nodes)
            {
                cout<<" "<<node;
            }
            cout<<endl;
            int op;
            cin>>op;
            if(op == 1)
            {
                nodes.insert(i);
            }
        }
    }
    cout<<"! "<<nodes.size()<<" ";
    for(auto node:nodes)
    {
        cout<<node<<" ";
    }
    cout<<endl;
    return 0;
}

标签:环上,int,题解,询问,mid,CF1854D,nodes,op
From: https://www.cnblogs.com/yuhang-ren/p/17635156.html

相关文章

  • P2034题解
    P2034题解题目描述给定一行\(n\)个非负整数\(a_1\cdotsa_n\)。现在你可以选择其中若干个数,但不能有超过\(k\)个连续的数字被选择。你的任务是使得选出的数字的和最大。题解正难则反,考虑将原问题转化为从\(a\)中选若干数使得,任意两数差不大于\(k\),求答案最小。观察......
  • ZS Shuffles Cards 题解
    ZSShufflesCards题解我们把每一次抽一些数字牌再抽到joker视作一局游戏。每局期望轮数首先考虑\(f_i\)表示每一局游戏抽出\(i\)张牌的概率。那么就是先抽出\(i-1\)张数字牌,再抽出一张joker。概率就是:\[f_i=\fracm{n+m-i+1}\prod_{k=0}^{i-2}......
  • CF1858A Buttons题解
    思路我们可以让两人先拿\(c\)里面的,因为\(a\)和\(b\)肯定是自己的,那么公共的“我”也要抢的越多越好,所以我们都要先拿\(c\)里面的。如果\(c\)是奇数,那么先手一定多拿\(1\)个\(c\)里面的,相当于先手可以拿\(a+1\)个,后手可以拿\(b\)个;如果\(c\)是偶数,那么两......
  • CF1858C Yet Another Permutation Problem 题解
    思路这个题是一个简单的构造题。竟然比T2简单,也是少见我们可以首先从\(1\)开始不断乘以\(2\),像这样:\(1,2,4,8,16\cdots,2^x\),直到什么时候超过\(n\)就停止。这样相邻两个数字就可以凑出\(1,2,4,6,\cdots,2^{x-1}\),保证两两不同。然后我们可以从\(3\)开始不......
  • 2023“钉耙编程”中国大学生算法设计超级联赛(9)- 1003 Reasoning 题解
    题目翻译基本符号有一推理系统,其中有这些符号:括号\((\)和\()\);逻辑连接词\(\lnot\)和\(\rightarrow\);全称量词\(\forall\);变量\(u\simz\);常量\(a\sime\);函数\(f\simh\);谓词\(P\simT\)。这些符号是构成系统的基础,他们之间能够组合出一些其他概念:项(term......
  • P3572题解
    P3572题解题面翻译有\(n\)棵树排成一排,第\(i\)棵树的高度是\(d_i\)。有\(q\)只鸟要从第\(1\)棵树到第\(n\)棵树。当第\(i\)只鸟在第\(j\)棵树时,它可以飞到第\(j+1,j+2,\cdots,j+k_i\)棵树。如果一只鸟飞到一颗高度大于等于当前树的树,那么它的劳累值会增......
  • CF1060E Sergey and Subway 题解
    题面由题意可知,在原图中经过边数为\(2\)的一对点,在新图中经过边数为\(1\)。所以每对点在新图中的距离为:\[\begin{aligned}\lceil\frac{dis(i,j)}{2}\rceil=\frac{dis(i,j)+dis(i,j)\;mod\;2}{2}\end{aligned}\]那么我们只需在原图上求出任意两点距离之和并加上\(dis......
  • 国标GB28181视频监控平台EasyGBS国标平台无法播放,抓包返回ICMP的问题解决方案
    国标GB28181视频平台EasyGBS是基于国标GB/T28181协议的行业内安防视频流媒体能力平台,可实现的视频功能包括:实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。国标GB28181视频监控平台部署简单、可拓展性强,支持将接入的视频流进行全终端、全平台分发,分发的......
  • winform窗体闪烁问题解决方式
    winform窗体闪烁问题解决方式1、使用窗体双缓冲SetStyle(ControlStyles.UserPaint|ControlStyles.AllPaintingInWmPaint|ControlStyles.OptimizedDoubleBuffer,true);UpdateStyles();窗体的DoubleBuffered 指示是否对控件进行双缓存处理。2、使用CreateParams的使用......
  • CF1858C Yet Another Permutation Problem 题解
    杂言赛时想到做法,结果调code把自己心态调炸了,所以来写一篇题解(恼)。另:此题与P9345夕阳西下几时回几乎相同,可以此练手。另另:本题多测,多测不清空,爆零两行泪。题意翻译\(a_1,a_2,\dots,a_n\)是\(1\)至\(n\)的一个排列,记\(d_i=\gcd(a_i,a_{i\bmodn+1})\)。构造一个......