2020-3-15模拟测验记录与题解

文章目录

      • 前言
      • 题面
        • T1
        • T2
        • T3
      • 题解
        • T1(咕咕咕)
        • T2
        • T3
      • 代码
        • T1(咕咕咕)
        • T2(咕咕咕)
        • T3

前言

吃到了暴力的甜头!

一道题都不会,全上暴力,结果 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 n1 条边连成一棵树。在两个点 x x x y y y 之间连边的代价是 v x xor ⁡ v y v_x \operatorname{xor} v_y vxxorvy。问最小生成树代价和期望。

n ≤ 50 n \leq 50 n50 m ≤ 20 m \leq 20 m20,答案对 998244353 998244353 998244353 取模。

T2

定义一类“好串”满足:

  1. 由数字 0 0 0 ~ 9 9 9 组成。
  2. 每个后缀的字典序严格大于原串的字典序。

一个长为 n n n 的“好串”提供 n 2 n^2 n2 的贡献,问长度 ≤ n \leq n n 的“好串”的贡献和。

n ≤ 1 0 10 n \leq 10^{10} n1010。答案对 998244353 998244353 998244353 取模。

T3

一个 n n n 层的大楼,你有 k k k 个蛋,每次尝试你可以在任意一层摔蛋,在层数 > x >x >x 0 ≤ x ≤ n 0 \leq x \leq n 0xn)的地方摔蛋会把蛋摔碎,否则可以重复使用。你不知道 x x x,你现在要确保能测出 x x x,问最少尝试次数。多组询问。

T ≤ 1 0 5 T \leq 10^5 T105 k ≤ 64 k \leq 64 k64 n ≤ 1 0 18 n \leq 10^{18} n1018

题解

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} dpi1,j1 层时能够探测到。

如果蛋没碎,你就会往上探测 x x x,这时你少了一次尝试,没少蛋,所以你能保证上面最多只有 d p i − 1 , j dp_{i-1,j} dpi1,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=dpi1,j1+dpi1,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 k3 时通过探索发现答案 < 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×1065,然后 d p 2 dp2 dp2 数组开到 2 × 1 0 5 ∗ 64 2 \times 10^5 * 64 2×10564,dp 手法均如之前所述。这样就能在时空限制内分别计算出每个 k k k 下的 dp 数组,然后做就完事了。

刨去个性化的分段预处理,复杂度 O ( T log ⁡ n ) O(T\log n) O(Tlogn)

代码

T1(咕咕咕)

T2(咕咕咕)

T3

#include 
#include 
#include 
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;
}

你可能感兴趣的:(OI)