【解题报告】2019正睿Day2

1.stone

根据期望的线性性,答案 E(t) = P2 + P3 + · · · + Pn + 1,其中 Pi 是第 i 堆石子在第 1 堆之前被取走的概率。
考虑第 i 堆,可以发现其他堆都不会影响这两堆,因此相当于只要考虑只有这两堆的情况,因此概率即为aia1+ai\frac{ai}{a1 + ai}
因此答案即为i=2naia1+ai+1\sum_{i=2}^{n}{\frac{ai}{a1 + ai}+1},直接计算即可。
时间复杂度: Θ(n)。

2.memory

对于每个 x,考虑如果用它来压缩那么可以达到的最优解,设为 F(x)。
假如给定一个 x,想要求 F(x),那么可以通过二分贪心在 Θ(n log2 n) 的时间内算出。
直接对于所有 x 都暴力计算即可得到 Θ(nm log2 n) 的复杂度,可以通过子任务1, 2。
如果我们以随机顺序遍历所有 x,则在期望情况下,新的 F(x) 比当前所有F 的值都要小的 x 个数的期望应该是 ∑ 1/i,这是调和级数,为 Θ(ln n)。
于是我们只对于这些 x 二分即可:我们令答案为 ans − 1 并贪心,即可判断当前的 x 是否满足条件。
时间复杂度: Θ(nm + n ln n log2 n)。

使最大值最小,很显然这是一个二分。

60分做法

这个部分分还是非常好拿的,只需要枚举每一个x并二分求答案即可。

时间复杂度:O(nmlogn)O(nmlogn)

100分做法

其实就是再60分做法的基础上做一个类似与搜索的最优化剪枝的优化。

我们可以发现,当我们枚举到一个x的时候,当且仅当在a[i]=(a[i]+x)%ma[i]=(a[i]+x)\%m的基础上check(ans-1)成立(ans为区间[1,x-1]求得的最小值),这个x才有可能对答案产生贡献,所以我们在每次二分前根据check(ans-1)的情况去决定是否对x进行二分答案即可。

注意,为了防止自己被卡,最好将x的枚举顺序打乱后再进行枚举。

code:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<algorithm>
#define ll long long
#define INF 0x7fffffff
#define re register
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define qwq printf("qwq\n");

using namespace std;

int read()
{
	register int x = 0,f = 1;register char ch;
	ch = getchar();
	while(ch > '9' || ch < '0'){if(ch == '-') f = -f;ch = getchar();}
	while(ch <= '9' && ch >= '0'){x = x * 10 + ch - 48;ch = getchar();}
	return x * f;
}

int n,m,k,l,r,ti,mid,ans,sum,a[100005],b[100005],c[100005];

bool check(int l)
{
	int cnt = 1; sum = 0;
	for(int i = 1; i <= n; i++)
	{
		if(sum + b[i] > l) cnt++,sum = 0;
		sum += b[i];
		if(cnt > k) return false;
	}
	return true;
}

int cmp(int a,int b) {return rand() % 2;}

int main()
{
	n = read(); m = read(); k = read();
	srand(time(0));
	for(int i = 1; i < m; i++) c[i] = i;
	sort(c + 1, c + m,cmp);
	for(int i = 1; i <= n; i++) a[i] = read();
	ans = INF;
	for(int i = 1; i < m; i++)
	{
		int y = c[i];
		l = 0;
		for(int j = 1; j <= n; j++) b[j] = (a[j] + y) % m,sum += b[j],l = max(l,b[j]);
		r = min(ans,sum);
		while(l < r)
		{
			mid = (l + r) >> 1;
			if(check(mid)) r = mid,ans = min(ans,r);
			else l = mid + 1;
		}
	}
	printf("%d\n",ans);
    return 0;
}

3.subset

显然我们只会选择 n 的约数,因为如果选了其他的那么最小公倍数一定不是 n。
考虑对 n 进行分解,设 n = pα 1 1pα 2 2 · · · pα kk。那么我们要求选的数字满足:对于每个 i ∈ [1, k],均存在一个数在 pi 中是 0 次方,也存在一个数在 pi 中是 αi 次方。
直接计算并不容易,考虑容斥原理:如果只有一个 pα,那么我们用所有情况去掉不存在 0 次方的情况,再去掉不存在 α 次方的情况,然后加上都不存在的情况即可。
然后对于原问题,我们可以 Θ(4k) 枚举每个质因数是上面四种情况中的哪一种,然后计算出这种情况下的方案数即可。时间复杂度: Θ(4ω(n)ω(n)),可以通过子任务1, 2。
考虑如何进行优化。注意到对于第一种情况,方案数为 (αi + 1),第二种和第三种情况都是 αi,而第四种是 (αi − 1)。因此,我们可以转而枚举方案数的可能性,这只有三种,因此枚举的复杂度从 Θ(4k) 降到了 Θ(3k)。
值得一提的是,这里对 n 进行分解是本题的难点: Θ(√n) 的复杂度并不可以接受,但我们并不需要真的得到 p1, p2, · · · , pk,而是只要知道 α1, α2, · · · , αk 即可。
我们考虑先分解出所有 pi ≤ √3 n 的 pα i i,那么剩余部分只有 p, p2, pq 三种,其中p, q 都是质数。
那么我们先使用 Miller Rabin 算法判断 p 的情况,然后开根号后平方判断 p2 的情况,剩余的情况就是 pq 的情况了。
时间复杂度: Θ(√3 n + 3ω(n)ω(n))。

posted @ 2019-10-15 18:00  AuroraPolaris  阅读(133)  评论(0编辑  收藏  举报