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 的做法,似乎是根据接人送人的顺序来搞的,参考这里。
要看记搜的第一种做法则可以参考这里。