Contest 984

A

先手取最大,后手取最小,答案就是第 (leftlceilfrac{n}{2} ight ceil) 小的数。

nth_element 可以做到 (Oleft(n ight))

B

模拟,时间复杂度 (Oleft(nm ight))

C

如果约分后 (q) 中存在某个质因数 (b) 中没有,则无法除尽,比较显然就不证了,可以对着十进制想一想。

但是太大了根号分解质因数会爆炸怎么办?

发现我们并不关心具体的质因数,我们只需要将他们去掉,这不禁让人想到 (gcd)

于是就有了一个比较慢的做法,(q) 每次除以 (gcdleft(q,b ight)),如果 (gcd=1) 了就停下了,此时如果 (q)(1) 就能整除了。

时间复杂度先鸽着,可能会 ( exttt{TLE})

考虑能否更进一步优化。在做一次 (gcd) 的时候,我们发现 (b) 中有的质因数如果在 (gcd) 中没出现,那么它在 (q) 中必定也没出现。也就是说有贡献的质因数一定在 (gcd) 中出现了,所以我们可以直接将 (b)(gcdleft(q,b ight)) 替换,这样的时间复杂度先鸽着。

代码贴一下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read()
{
	ll A;
	bool K;
	char C;
	C=A=K=0;
	while(C<'0'||C>'9')K|=C=='-',C=getchar();
	while(C>'/'&&C<':')A=(A<<3)+(A<<1)+(C^48),C=getchar();
	return(K?-A:A);
}
ll gcd(ll _,ll __)
{
	return(!_?__:gcd(__%_,_));
}
bool pd(ll x,ll y)
{
	return(gcd(x,y)==1?x==1:pd(x/gcd(x,y),gcd(x,y)));
}
int main()
{
	int n;
	ll p,q,b;
	n=read();
	while(n--)
	{
		p=read(),q=read(),b=read();
		q/=gcd(p,q);
		puts((pd(q,b)?"Finite":"Infinite"));
	}
	return 0;
}

回寝室的时候问了下马阿克,发现我傻逼了……只要判一下 (b^{log q}) 能否被 (q) 整除就好了,这样的时间复杂度是 (Oleft(nlog q ight)) 的(瓶颈反而在于约分和算出 (log q))。

D

很有趣的一道 DP 题。

对于一个长度为 (len) 的区间,被它包含的所有长度小于等于 (len) 的区间都能取到。

所以我们可以令 (a_{i,j}) 表示长度为 (i),以 (j) 作为左端点的区间的答案。

发现所有长度小于等于 (i-1) 的区间在 (left[j,j+i-2 ight])(left[j+1,j+i-1 ight]) 中至少能取到一次。

现在只要解决新加入的一个区间 (left[j,j+i-1 ight]),即快速算出 (fleft(j o j+i-1 ight))

模拟出整个倒三角形,其实 (fleft(j o j+i-1 ight)) 就是第 (i) 行第 (j) 列的数。

时间复杂度 (Oleft(n^2+q ight))

E

卡常好题。

首先要观察到人进入电梯的顺序是固定的,可以搞进状态里。而楼层和容纳人数很少,也可以搞进状态里。

此处要注意,在电梯中,我们只关心一个人的目标楼层,而不关心他的具体编号。

(dp_{now,high,pos1,pos2,pos3,pos4,0/1}) 表示前 (now) 个人,当前楼层为 (high),电梯里的四个人的目标楼层(为 (0) 代表当前位置没人)以及当前电梯必须向上/下移动。

最后一维是为了防止出现反复横跳的情况。

发现转移比较恶心,不知道具体的转移顺序,考虑记忆化搜索。但问题又来了,(2000 imes 9 imes 10^4 imes 2=360000000),这么大的数组拿头开。

此处有两个解决方案:

  • 强行限制状态,将目标楼层从大到小排序或者从小到大排序,这样方案数按不同数的个数算一下就是 (inom{10}{4}+inom{10}{3} imes 3+inom{10}{2} imes 3+inom{10}{1}=715)
    只需要开 (2000 imes 9 imes 715 imes 2=25740000) 大小的数组即可。
  • 滚动数组优化记忆化搜索。依次枚举人,第一维只与上一个有关。所以当前人进入电梯时从上一个转移过来,其它情况直接记搜。
    只需要开 (9 imes 10^4 imes 2^2=360000) 大小的数组即可。

当然你可以两个一起用。

显然的,这两种方法都非常之慢,尤其是第二种。如果你写了第二种的话,要加上四个优化(第一种没试过不清楚):

  • 当有人下电梯时,递归下去后直接返回。因为能下电梯显然下,代价一样,这样不会占着位子。
  • 当有人上电梯时,递归下去后直接返回。因为能上电梯显然上,代价一样,避免下次特意回来接。
  • 尽量把递归下去后算出的值存下来,避免再次调用。虽然后面递归时会直接返回,但递归本身非常耗时间
  • 火车头。

前两个加上后可以免去很多多余的取最小值步骤。

时间复杂度 (Oleft(n ight))(2000) 最慢跑了 (2979ms) 你敢信?

另:有直接 DP 的做法,似乎是根据接人送人的顺序来搞的,参考这里

要看记搜的第一种做法则可以参考这里

原文地址:https://www.cnblogs.com/May-2nd/p/14023206.html