这道题刚入手,我的第一反应就是:暴力模拟!
没啥好说的,直接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 k≤109,n≤105,m≤102
这模拟只能骗到 20 20 20 分(以内)啊?
我于是开始找规律,结果啥规律也找不出,对于每个数据,都有不同的规律,循环节也不好计算。
那——要不使用判断循环节?用哈希判重?
不行。如果循环节太长了,就大于 100 100 100 ,也会把这算法卡掉。这个大概是 30 − 40 30-40 30−40分的思路。
欸?能不能一次做多组操作?能不能一次做 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);
}