前言
吃到了暴力的甜头!
一道题都不会,全上暴力,结果 rk3,只位列两位切了题的大佬之后。
题也不难,但是我还是没做出来。
题面
T1
n n n 个点,每个点有一个整数权值 v i v_i vi,在 [ 0 , 2 m ) [0,2^m) [0,2m) 内随机。现在你要把这 n n n 个点用 n − 1 n-1 n−1 条边连成一棵树。在两个点 x x x 与 y y y 之间连边的代价是 v x xor v y v_x \operatorname{xor} v_y vxxorvy。问最小生成树代价和期望。
n ≤ 50 n \leq 50 n≤50, m ≤ 20 m \leq 20 m≤20,答案对 998244353 998244353 998244353 取模。
T2
定义一类“好串”满足:
- 由数字 0 0 0 ~ 9 9 9 组成。
- 每个后缀的字典序严格大于原串的字典序。
一个长为 n n n 的“好串”提供 n 2 n^2 n2 的贡献,问长度 ≤ n \leq n ≤n 的“好串”的贡献和。
n ≤ 1 0 10 n \leq 10^{10} n≤1010。答案对 998244353 998244353 998244353 取模。
T3
一个 n n n 层的大楼,你有 k k k 个蛋,每次尝试你可以在任意一层摔蛋,在层数 > x >x >x( 0 ≤ x ≤ n 0 \leq x \leq n 0≤x≤n)的地方摔蛋会把蛋摔碎,否则可以重复使用。你不知道 x x x,你现在要确保能测出 x x x,问最少尝试次数。多组询问。
T ≤ 1 0 5 T \leq 10^5 T≤105, k ≤ 64 k \leq 64 k≤64, n ≤ 1 0 18 n \leq 10^{18} n≤1018。
题解
T1(咕咕咕)
T2
可以利用 OEIS 得到一个公式然后推一下杜教筛。。。
但是证明有待补充。
复杂度 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32)
T3
经典题 & 神题,orz。
把答案设在状态里的思想非常值得学习。
令 d p i , j dp_{i,j} dpi,j 表示尝试了 i i i 次,拥有 j j j 个蛋的情况下能保证探测出 x x x 的最大的 n n n。
转移时考虑这样一个情景:
你拥有 i i i 次尝试, j j j 个蛋,你在某一层摔了一个蛋。
如果蛋摔碎了,你就会往下探测 x x x,这时你少了一次尝试,少了一个蛋,所以你只能保证下面最多只有 d p i − 1 , j − 1 dp_{i-1,j-1} dpi−1,j−1 层时能够探测到。
如果蛋没碎,你就会往上探测 x x x,这时你少了一次尝试,没少蛋,所以你能保证上面最多只有 d p i − 1 , j dp_{i-1,j} dpi−1,j 层时能够探测到。
那么就很显然了: d p i , j = d p i − 1 , j − 1 + d p i − 1 , j + 1 dp_{i,j} = dp_{i-1,j-1} + dp_{i-1,j} +1 dpi,j=dpi−1,j−1+dpi−1,j+1。
已知 k k k 的情况下,只需要在处理好的 dp 数组上二分即可。
当然只有这些你是过不了题的,因为你肯定开不下这个 dp 数组。
首先用数学归纳法易得公式: d p i , j = ∑ x = 1 j C i x dp_{i,j} = \sum_{x=1}^{j} C_i^x dpi,j=∑x=1jCix( x > i x > i x>i 时令 C i x = 0 C_i^x = 0 Cix=0)。
用公式直接做是 O ( T k log n ) O(Tk\log n) O(Tklogn) 的,毒瘤出题人不放你过。有没有更优秀的性质?
我们发现:当 k k k 比较小的时候,尝试次数需要非常大这个值才能到达 n n n。研究一下,可以发现 k = 1 k=1 k=1 时答案就是 n n n, k = 2 k = 2 k=2 时答案为 ⌊ n ⌋ \lfloor \sqrt{n} \rfloor ⌊n⌋ 或 ⌊ n ⌋ + 1 \lfloor \sqrt{n} \rfloor +1 ⌊n⌋+1,具体算出值来试一下就好了。
而 k ≥ 3 k \geq 3 k≥3 时通过探索发现答案 < 2 × 1 0 6 < 2 \times 10^6 <2×106,且在快速减小,就变得可算了起来。
为了防止空间炸裂我们还是要分段。比方说我们令 d p 1 dp1 dp1 数组开到 2 × 1 0 6 ∗ 5 2 \times 10^6 * 5 2×106∗5,然后 d p 2 dp2 dp2 数组开到 2 × 1 0 5 ∗ 64 2 \times 10^5 * 64 2×105∗64,dp 手法均如之前所述。这样就能在时空限制内分别计算出每个 k k k 下的 dp 数组,然后做就完事了。
刨去个性化的分段预处理,复杂度 O ( T log n ) O(T\log n) O(Tlogn)。
代码
T1(咕咕咕)
T2(咕咕咕)
T3
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N1=2e6+1,K1=5,N2=2e4+1,K2=65;
long long dp1[N1][K1],dp2[N2][K2];
int main()
{
int i,j;
for(i=1;i<N1;i++)
{
for(j=1;j<K1;j++)
{
if(dp1[i-1][j-1]==-1||dp1[i-1][j]==-1) dp1[i][j]=-1;
else dp1[i][j]=dp1[i-1][j-1]+dp1[i-1][j]+1;
if(dp1[i][j]>=1e18) dp1[i][j]=-1;
}
}
for(i=1;i<N2;i++)
{
for(j=1;j<K2;j++)
{
if(dp2[i-1][j-1]==-1||dp2[i-1][j]==-1) dp2[i][j]=-1;
else dp2[i][j]=dp2[i-1][j-1]+dp2[i-1][j]+1;
if(dp2[i][j]>=1e18) dp2[i][j]=-1;
}
}
int T;
scanf("%d",&T);
while(T--)
{
long long n;
int k;
scanf("%lld%d",&n,&k);
if(k<=2)
{
if(k==1) printf("%lld\n",n);
else
{
int rem=sqrt(n*2);
long long calc=1ll*rem*(rem+1);
if(calc>=n*2) printf("%d\n",rem);
else printf("%d\n",rem+1);
}
}
else
{
int l=1,r;
if(k<K1) r=N1-1;
else r=N2-1;
while(l<r)
{
int mid=(l+r)>>1;
if(k<K1)
{
if(dp1[mid][k]==-1||dp1[mid][k]>=n) r=mid;
else l=mid+1;
}
else
{
if(dp2[mid][k]==-1||dp2[mid][k]>=n) r=mid;
else l=mid+1;
}
}
printf("%d\n",l);
}
}
return 0;
}