Live2D

Solution -「LOCAL」过河

\(\mathcal{Description}\)

  一段坐标轴 \([0,L]\),从 \(0\) 出发,每次可以 \(+a\)\(-b\),但不能越出 \([0,L]\)。求可达的整点数。

  \(L\le10^{12}\)\(1\le a,b\le10^5\)

\(\mathcal{Solution}\)

\(\mathcal{Case~1}\)

  考场上玄学操作,天知道为什么兔子签到的姿势如此诡异。

  显然先约 \(\gcd\)。我们从 \(0\) 次开始枚举 \(-b\) 的次数,设当前枚举到 \(k\),若 \(kb\equiv0\pmod a\),可以通过少一次 \(a\) 来完成,调出循环;否则,钦定 \(k\)\(-b\) 时,可达的整点一定是一个模 \(a\) 剩余类中连续的区间,即 \(l,l+a,l+2a,\cdots,r\)。我们只需要维护出 \((l,r)\),累加 \(\frac{r-l}a+1\) 就能得到答案。考虑多一次 \(-b\)\((l,r)\) 的影响,例如当 \(a=4,b=3\)

\[-~~~~-~~~~l~~~~-~~~~-~~~~-~~~~l+a~~~~-~~~~-~~~~-~~~~r~~~~- \]

  首先左移 \(b\) 位,类似位运算,出界的低位被销毁:

\[-~~~~-~~~~-~~~~l+a~~~~-~~~~-~~~~-~~~~r~~~-~~~~-~~~~-~~~~- \]

  这个时候需要判断是否存在 \(l+ka\)(可能 \(b\)\(a\) 的若干倍,不仅是一个 \(+a\) 可以拉回来的),若不存在,跳出。然后考虑更新 \(r\),它还能往后 \(+a\),一直顶满上限:

\[-~~~~-~~~~-~~~~l+a~~~~-~~~~-~~~~-~~~~r-a~~~-~~~~-~~~~-~~~~r \]

  于是模拟一下就解决了。复杂度 \(\mathcal O(a+b)\)

\(\mathcal{Case~2}\)

  可以打表证明当 \(a+b-1>L\),答案为 \(L+1\);否则暴力 DFS……

  兔子可能是个傻瓜,嗯。

\(\mathcal{Code}\)

// case 1
#include <cstdio>

typedef long long LL;

inline char fgc () {
	static char buf[1 << 17], *p = buf, *q = buf;
	return p == q && ( q = buf + fread ( p = buf, 1, 1 << 17, stdin ), p == q ) ? EOF : *p ++;
}

inline LL rint () {
	LL x = 0; char s = fgc ();
	for ( ; s < '0' || '9' < s; s = fgc () );
	for ( ; '0' <= s && s <= '9'; s = fgc () ) x = x * 10 + ( s ^ '0' );
	return x;
}

inline void wint ( const LL x ) {
	if ( 9 < x ) wint ( x / 10 );
	putchar ( x % 10 ^ '0' );
}

LL L, a, b;
bool vis[100005];

inline LL gcd ( const LL a, const LL b ) {
	return b ? gcd ( b, a % b ) : a;
}

int main () {
	freopen ( "river.in", "r", stdin );
	freopen ( "river.out", "w", stdout );
	L = rint (), a = rint (), b = rint ();
	int d = gcd ( a, b );
	a /= d, b /= d, L /= d;
	if ( a == 1 ) return wint ( L + 1 ), putchar ( '\n' ), 0;
	if ( b == 1 ) return wint ( a > L ? 1 : L + 1 ), putchar ( '\n' ), 0;
	LL ans = L / a + 1, l = 0, r = a * ( L / a );
	vis[0] = true;
	for ( int useb = 1; !vis[1ll * useb * b % a]; ++ useb ) {
		vis[useb * b % a] = true;
		l -= b;
		if ( l < 0 ) l += a * ( ( -l - 1 ) / a + 1 );
		if ( l + b > r ) break;
		r -= b;
		if ( L - r >= a ) r += a * ( ( L - r ) / a );
		if ( l > r ) break;
		ans += ( r - l ) / a + 1;
	}
	wint ( ans ), putchar ( '\n' );
	return 0;
}

\(\mathcal{Details}\)

  还陷在省选难度签不动道的怪圈里……这道题算上拍浪费了差不多 \(40min\)

  麻烦兔子醒醒啊!

posted @ 2020-08-26 21:10  Rainybunny  阅读(74)  评论(0编辑  收藏  举报