有$n$个点以及$m$只$doge$(大概是某种神奇生物?),第$i$只$doge$最初在第$a_i$个点,每次能向左或向右跳$b_i$个点。当两个$doge$在某位置相遇时,可以改为另一只$doge$跳动。求从第$1$只$doge$所在位置出发,至少经过多少步才能跳到第$2$只$doge$所在位置。

点此看题面

大致题意:\(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;
}
败得义无反顾,弱得一无是处