交互题训练1


交互题经典思路:二分


一道经典的题目便是猜数字。
Codeforces 1011D - Rocket
不过,这里面的人有可能撒谎,即大于有可能说成小于,小于有可能说成大于。
好在,它们说真假话为n个一个周期。
即第 i i i次说假话当且仅当 i   m o d   n i\ mod\ n i mod n时说假话。
真话同理。
猜的数在[1,1e9]内。 n ⩽ 30 n\leqslant 30 n30,需要60次内猜出来。
我们可以先采取n次每次问1,得出真谎话周期。
后面二分即可。

codeforces 862D和codeforces 714D均是这个经典思路的应用。

在[IOI 2018]highway中,二分的伟大神力被体现地淋漓尽致。
题意:
有个n个点,m条边的无向图。 n ⩽ 90000 n\leqslant 90000 n90000 m ⩽ 130000 m\leqslant 130000 m130000
有两种权值A,B( A < B A<B A<B)。你需要询问出起点S,T。
每次你可以给每条边赋上A或B的权值,然后询问S,T的最短路。
用不超过50次询问确定S,T。

首先,询问边权权全为A时的最短路。(以下我们把边权全为A时的最短路简称为最短路)
然后,对边的编号二分,找出一条在最短路上的边。
记为e1-e2
那么,用bfs找出e1,e2在原图上的最短路树。
可以看出,S,T到e1,e2的距离互不相同,并且S,T会选择较近的那个点。
那么e1,e2会分别形成一棵树,互不相交。
分别在e1,e2的树上bfs序上二分,找出S和T。
注意到,若两棵树的大小为s1,s2。
则需要询问的次数为 O ( ⌈ l o g 2 s 1 ⌉ + ⌈ l o g 2 s 2 ⌉ + ⌈ l o g 2 m ⌉ + 1 ) O(\lceil log_2s1\rceil+\lceil log_2s2 \rceil+\lceil log_2m \rceil+1) O(log2s1+log2s2+log2m+1)
由于 s 1 + s 2 ⩽ n s1+s2\leqslant n s1+s2n
故最多询问次数为 2 ⌈ l o g 2 n ⌉ + ⌈ l o g 2 m ⌉ − 1 2\lceil log_2n \rceil+\lceil log_2m \rceil-1 2log2n+log2m1
即最多50次。

 
#include "highway.h"
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int n,m,A,B;
ll Ans;
#define V 90010
#define E 130010
struct Edge{
    int a,b;
}edge[E];
int head[V],v[E<<1],w[E<<1],nxt[E<<1],tot=0;
int val[E];
inline void add_edge(int s,int e,int t){
    tot++;v[tot]=e;w[tot]=t;nxt[tot]=head[s];head[s]=tot;
    tot++;v[tot]=s;w[tot]=t;nxt[tot]=head[e];head[e]=tot;
}
 
int dis1[V],dis2[V],fr1[V],fr2[V],Q1[V],Q2[V];
int Q[V],hd,tl,cnt1,cnt2;
void Bfs(int S,int *dis,int *fr,int *Q){
    memset(dis,-1,sizeof(int)*n);
    hd=tl=0;
    Q[tl++]=S;dis[S]=0;
    while(hd vec;
inline ll Ask(){
    vec.clear();
    for(int i=0;i e1,vector e2,int val1,int val2){
    A=val1;B=val2;n=N;m=e1.size();
    for(int i=0;i>1;
        if(Judge1(mid+1))l=mid+1;
        else r=mid;
    }
    memset(fr1,-1,sizeof(fr1));
    memset(fr2,-1,sizeof(fr2));
    Bfs(edge[l].a,dis1,fr1,Q1);
    Bfs(edge[l].b,dis2,fr2,Q2);
    tot=0;memset(head,0,sizeof(int)*n);
    for(int i=0;i>1;
        for(int i=0;i>1;
        for(int i=0;i

你可能感兴趣的:(日常习题)