大致题意: 有(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),每个状态只会被处理一次,因此我们只需知道状态总数,即可知道复杂度。
- 对于(ble sqrt n),在(a=1sim n)的情况下,最多只有(nsqrt n)种状态。
- 对于(b>sqrt n),则每一个(b)所能对应的(a)的数量小于(sqrt n),即便全部(m)只(doge)的(b)都大于(sqrt n),最多也只有(msqrt 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;
}