A. 排列最小生成树 (pmst)
大家都没签上的签到
调参调到 110 能过?!但赛时用 set 这个大常数选手存的边,挂了个 log,所以跑不动大样例。
正解:
发现只按把编号相邻的点连边构成一条链,此时所有边的长度都 \(\le n\),所以无论如何我们都能保证最小生成树每条边的边权 \(\le n\)。
那么我们只把边权 \(\le n\) 的边连上即可。边权 \(\le n\) 需要满足 \(abs(i-j)\) 和 \(abs(p_i-p_j)\) 至少有一个 \(\le \sqrt n\)。所以每一个点建上 \(2\times \sqrt n\) 条边即可。
边权最多只有 \(n\),所以开个 vector 数组 \(v_i\) 存边权为 \(i\) 的所有边,这样避免排序的 log 复杂度。
B. 卡牌游戏 (cardgame)
签到
设 \(lcm\) 为 \(n\) 和 \(m\) 的最小公倍数,\(gcd\) 为 \(n\) 和 \(m\) 的最大公因数,显然 \(lcm\) 轮时正好为一个循环。
而这前 \(lcm\) 轮内保证有每两个人最多比一次。
\(n=k_1\times gcd\),\(m=k_2\times gcd\),所以在一次循环中 \(a_i\) 和 \(b_j\) 比较时一定有 \(i\% gcd=j \% gcd\),并且对于一个 \(i\) 会对应所有的 \(j\ (if\ j\%gcd=i\%gcd)\),所以把 1~n 中所有模 \(gcd\) 余数相同的存一起,1~m 中所有模 \(gcd\) 余数相同的存一起。对于所有的 \(i\in [1,n]\),计算 1~m 中 \(i\% mod\) 的那个桶里有多少小于自己的,等于自己的,大于自己的就好了。
共有 \(\lfloor \frac {n\times m} {lcm} \rfloor\) 次循环,求出一次循环 A 赢的次数、B 赢的次数,乘上循环次数就好了。
对于 \(n\times m \% lcm\) 的那部分,暴力模拟就好。
C. 比特跳跃 (jump)
二进制运算性质
发现其实是一题三做。
-
\(and\) 部分
发现大部分数都可以做到答案为 0,只有在 \(n\) 的二进制表示全为 1 的时候特殊,这个时候除了 \(n\) 之外的为 0,把与 \(n\) 相关的边建上跑最短路求出的 \(dis_n\) 为 n 的答案;
-
\(xor\) 部分
将异或运算看成是每一位上相加但不进位,所以每次改变二进制表示下的一位再连边连向原数就好了
-
\(or\) 部分
或的数越多,权值就越大,所以一个数只由 1 或者自己二进制下 1 的子集表示的数转移过来较优。
先把 1 连向每个点和那 \(m\) 条边跑一边最短路,然后对于每个数用它二进制下 1 的子集表示的数的权值更新自己,同样每次只改变二进制的一位。
把当前每个数的权值作为边权存进 Dij 的堆里,再跑一遍最短路。