Codeforces交互题训练1&&[IOI 2018]highway


交互题经典思路:二分


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

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

在[IOI 2018]highway中,二分的伟大神力被体现地淋漓尽致。
题意:
有个n个点,m条边的无向图。 n 90000 n\leqslant 90000 m 130000 m\leqslant 130000
有两种权值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)
由于 s 1 + s 2 n s1+s2\leqslant n
故最多询问次数为 2 l o g 2 n + l o g 2 m 1 2\lceil log_2n \rceil+\lceil log_2m \rceil-1
即最多50次。

 
#include "highway.h"
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
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<tl){
        int u=Q[hd];
        hd++;
        for(int i=head[u];i;i=nxt[i])
            if(dis[v[i]]==-1){
                dis[v[i]]=dis[u]+1;
                fr[v[i]]=i;
                Q[tl++]=v[i]; 
            }
    }
}
 
vector<int> vec;
inline ll Ask(){
    vec.clear();
    for(int i=0;i<m;++i)
        if(val[i]==A)vec.push_back(0);
        else vec.push_back(1);
    return ask(vec);
}
 
inline bool Judge1(int mid){
    for(int i=mid;i<m;++i)val[i]=B;
    for(int i=0;i<mid;++i)val[i]=A;
    if(Ask()!=Ans)return true;
    else return false;
}
 
void find_pair(int N,vector<int> e1,vector<int> e2,int val1,int val2){
    A=val1;B=val2;n=N;m=e1.size();
    for(int i=0;i<m;++i){
        edge[i]=(Edge){e1[i],e2[i]};
        add_edge(e1[i],e2[i],i);
    }
    int l,r;
    for(int i=0;i<m;++i)val[i]=A;
    Ans=Ask();
    l=0;r=m-1;
    while(l<r){
        int mid=(l+r)>>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<n;++i){
        fr1[i]=w[fr1[i]];
        fr2[i]=w[fr2[i]];
    }
    for(int i=0;i<n;++i)
        if(dis1[i]<dis2[i]){
            if(fr1[i]!=-1)add_edge(edge[fr1[i]].a,edge[fr1[i]].b,fr1[i]),cnt1++;
        }
        else if(dis2[i]<dis1[i]){
            if(fr2[i]!=-1)add_edge(edge[fr2[i]].a,edge[fr2[i]].b,fr2[i]),cnt2++;
        }
    Bfs(edge[l].a,dis1,fr1,Q1);
    Bfs(edge[l].b,dis2,fr2,Q2);
    for(int i=0;i<n;++i){
        fr1[i]=w[fr1[i]];
        fr2[i]=w[fr2[i]];
    }
    int ed=l;
    l=0;r=cnt1-1;
    while(l<r){
        int mid=(l+r)>>1;
        for(int i=0;i<m;++i)val[i]=B;
        val[ed]=A;
        for(int i=1;i<=mid;++i)val[fr1[Q1[i]]]=A;
        for(int i=1;i<cnt2;++i)val[fr2[Q2[i]]]=A;
        if(Ask()!=Ans)l=mid+1;
        else r=mid;
    }       
    int S=Q1[l];
     
    l=0;r=cnt2-1;
    while(l<r){
        int mid=(l+r)>>1;
        for(int i=0;i<m;++i)val[i]=B;
        val[ed]=A;
        for(int i=1;i<=mid;++i)val[fr2[Q2[i]]]=A;
        for(int i=1;i<cnt1;++i)val[fr1[Q1[i]]]=A;
        if(Ask()!=Ans)l=mid+1;
        else r=mid;
    }
    int T=Q2[l];
    answer(S,T);
}

猜你喜欢

转载自blog.csdn.net/ezoilearner/article/details/88073869