大致题意: 有\(n\)个点以及\(m\)只\(doge\)(大概是某种神奇生物?),第\(i\)只\(doge\)最初在第\(a_i\)个点,每次能向左或向右跳\(b_i\)个点。当两个\(doge\)在某位置相遇时,可以改为另一只\(doge\)跳动。求从第\(1\)只\(doge\)所在位置出发,至少经过多少步才能跳到第\(2\)只\(doge\)所在位置。
前言
emmm,这道题说是分块题,结果最后好像只是用分块的思想来证明复杂度?
\(BFS\)跑得飞快,只是这题卡\(set\),害得我不得不改成\(bitset\)。
简单粗暴的\(BFS\)
我们设状态\((a,b)\),表示当前的\(doge\)在第\(a\)个点,跳动能力是\(b\)。
然后要注意对于每一个点,只有当我们第一次\(BFS\)到时,需要判断是否改为由这个点上的\(doge\)跳动,因为越早改肯定越优。
复杂度证明
考虑由于是\(BFS\),每个状态只会被处理一次,因此我们只需知道状态总数,即可知道复杂度。
- 对于\(b\le \sqrt n\),在\(a=1\sim n\)的情况下,最多只有\(n\sqrt n\)种状态。
- 对于\(b>\sqrt n\),则每一个\(b\)所能对应的\(a\)的数量小于\(\sqrt n\),即便全部\(m\)只\(doge\)的\(b\)都大于\(\sqrt n\),最多也只有\(m\sqrt n\)种状态。
于是\(BFS\)的复杂度得到了证明。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 30000
#define pb push_back
using namespace std;
int n,m,a[N+5],b[N+5],vis[N+5];vector<int> V[N+5];bitset<N+5> s[N+5];
struct S//记录状态
{
int a,b,t;I S(CI x=0,CI y=0,CI k=0):a(x),b(y),t(k){}
};vector<S> q;
I int BFS()
{
if(a[1]==a[2]) return 0;
#define Try(A) (!s[A.a].test(A.b)&&(s[A.a].set(A.b),++T,q.pb(A),0))//判断是否重复,若不重复则加入队列
#define New(p,f) for(vis[p]=1,i=0,sz=V[p].size();i^sz;++i) nk=S(p,b[V[p][i]],f),Try(nk);//将第p个点所有doge加入队列
RI H=0,T=-1,i,sz;S k,nk;New(a[1],0);W(H<=T)//BFS
{
if(k=q[H++],k.a>=k.b)//向左跳
{
if(k.a-k.b==a[2]) return k.t+1;if(!vis[k.a-k.b]) New(k.a-k.b,k.t+1);
nk=S(k.a-k.b,k.b,k.t+1),Try(nk);
}
if(k.a+k.b<n)//向右跳
{
if(k.a+k.b==a[2]) return k.t+1;if(!vis[k.a+k.b]) New(k.a+k.b,k.t+1);
nk=S(k.a+k.b,k.b,k.t+1),Try(nk);
}
}return -1;//无解
}
int main()
{
RI i;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",a+i,b+i),V[a[i]].pb(i);
return printf("%d",BFS()),0;
}