交互题经典思路:二分
一道经典的题目便是猜数字。
Codeforces 1011D - Rocket
不过,这里面的人有可能撒谎,即大于有可能说成小于,小于有可能说成大于。
好在,它们说真假话为n个一个周期。
即第
次说假话当且仅当
时说假话。
真话同理。
猜的数在[1,1e9]内。
,需要60次内猜出来。
我们可以先采取n次每次问1,得出真谎话周期。
后面二分即可。
codeforces 862D和codeforces 714D均是这个经典思路的应用。
在[IOI 2018]highway中,二分的伟大神力被体现地淋漓尽致。
题意:
有个n个点,m条边的无向图。
有两种权值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。
则需要询问的次数为
由于
故最多询问次数为
即最多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);
}