[USACO20FEB]Swapity Swapity Swap

这道题刚入手,我的第一反应就是:暴力模拟!

没啥好说的,直接reverse k k k 遍,时间复杂度 O ⁡ ( n m k ) \operatorname{O}(nmk) O(nmk) ,还似乎吃得消。

结果我看了看数据范围,就吓得一身冷汗。。。

k ≤ 1 0 9 , n ≤ 1 0 5 , m ≤ 1 0 2 k\le 10^9,n\le10^5,m\leq10^2 k109,n105,m102

这模拟只能骗到 20 20 20 分(以内)啊

我于是开始找规律,结果啥规律也找不出,对于每个数据,都有不同的规律,循环节也不好计算。

那——要不使用判断循环节?用哈希判重?

不行。如果循环节太长了,就大于 100 100 100也会把这算法卡掉。这个大概是 30 − 40 30-40 3040分的思路。

欸?能不能一次做多组操作?能不能一次做 l l l?使得时间复杂度大大降低?

理论上可行。比如说你把这个 l l l 设成 1 0 5 10^5 105 以上,效率就会大大提升。

但是,实际上这个算法还是很慢。 O ( n m k l ) O(\frac{nmk}{l}) O(lnmk) 这个复杂度其实还是很高,而且万一这个循环节不在第 l l l 的整数倍位上,这样还要加上一个 n m l nml nml

其实把 l l l 设成 k \sqrt{k} k 这种数,时间复杂度会大大减少,到达 O ( n m k ) O(nm\sqrt{k}) O(nmk )这种级别。

既然一次做均匀的 k l \frac{k}{l} lk 次操作不行,我能不能做不均匀的操作?比如对于 i i i 次,就做 2 i 2^i 2i 组操作?能否实现?

恭喜你,你懂得了这道题要使用倍增

这个倍增,顾名思义,就是成倍增长

举个例子:倍增求LCA

LCA是啥?就是树上两个点的最近公共祖先。

然后我们就可以使用倍增。

指定两个节点,让你每个节点往上跳 2 i 2^i 2i辈(更准确的说法是每个节点往上 2 i 2^i 2i代祖先),如果碰到了,就可以更加细化,往下继续二分。

推荐做LCA模板题嗷。

倍增必要的小工具:

把一个数二进制分解。

首先,可以 O ( 1 ) O(1) O(1) 求出这个数的二进制最高位(也可以不用求,对于 1 0 9 10^9 109以内的数据,从 30 30 30往下搜就可以,对于 1 0 18 10^{18} 1018数量级, 63 63 63往下搜绰绰有余)。

然后让 i i i 指针往下搜,如果这个数大于等于 2 i 2^i 2i,就减去 2 i 2^i 2i。顺便让另外求值的那个变量与这个 f [ i ] f[i] f[i]有关联。

我们可以确定一个倍增数组 f [ i ] [ j ] f[i][j] f[i][j]

表示 a a a 数组在经过了 2 i 2^i 2i次操作后所生成的新的 a j a_j aj

然后预处理完成后,就开始二进制拆分。

具体看代码。

	in(n,m,k);
	Fu(i,1,n)f[0][i]=i;
	Fu(i,1,m)in(t1,t2),reverse(a+t1,a+t2+1);
	Fu(i,1,30)Fu(j,1,n)f[i][j]=f[i-1][f[i-1][j]];
	Fu(i,1,n)
	{
		int t=k,x=i;
		Fd(j,30,0)
		if(t>=(1<<j))t-=(1<<j),x=f[j][x];
		out(x);
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值