[BZOJ4900/UOJ#297][CTSC2017]密钥(乱搞?!)

Address

洛谷P3770
BZOJ4900
UOJ#297
LOJ#2261

Solution

以第一个问题为例,可以设:
a [ i ] = { 1 i P 1 i P a[i]=\begin{cases}1&i\in P\\-1&i\notin P\end{cases}
然后设 a a 的前缀和:
s u m [ i ] = s u m [ i 1 ] + a [ i ] sum[i]=sum[i-1]+a[i]
设起点为 x x a [ x ] = 1 a[x]=-1 ),那么特征值显然为:
i = x + 1 2 k + 1 [ s u m [ i ] s u m [ x ] > 0 ] + i = 1 x 1 [ 1 s u m [ x ] + s u m [ i ] > 0 ] \sum_{i=x+1}^{2k+1}[sum[i]-sum[x]>0]+\sum_{i=1}^{x-1}[-1-sum[x]+sum[i]>0]
方括号里的东西可以表示成:
s u m [ i ] > s u m [ x ] sum[i]>sum[x]
s u m [ i ] > s u m [ x ] + 1 sum[i]>sum[x]+1
于是我们用树状数组可以做到 O ( k log k ) O(k\log k) 的复杂度。
k k 1 0 7 10^7 级别,我们需要想办法优化。
发现 s u m [ i ] sum[i] 等于 s u m [ i 1 ] + 1 sum[i-1]+1 s u m [ i 1 ] 1 sum[i-1]-1
所以只需要维护一个指针 p p ,每次查询时根据 a [ i ] a[i] 的值将指针左移或右移,同时维护 i i 的右边有多少个 s u m > p sum>p 及左边有多少个数大于 p + 1 p+1 。但同时 i i 的值会变,所以 s u m sum 的查询范围也会变(相当于插入 / 删除 一个数),根据与 p p p + 1 p+1 的大小关系处理。
第三问一样处理,这时 a [ i ] = { 1 i P 1 i P a[i]=\begin{cases}-1&i\in P\\1&i\notin P\end{cases} ,找答案时找 a [ x ] = 1 a[x]=1 的位置 x x ,在 x x 位置的特征值为:
i = x + 1 2 k + 1 [ s u m [ i ] s u m [ x ] > 0 ] + i = 1 x 1 [ 1 s u m [ x ] + s u m [ i ] > 0 ] \sum_{i=x+1}^{2k+1}[sum[i]-sum[x]>0]+\sum_{i=1}^{x-1}[1-sum[x]+sum[i]>0]
复杂度 O ( k ) O(k)

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)

const int E = 2e7 + 1, N = 2e7 + 7, M = N << 1;

int p[N], seed, n, k, S, sum[N], ans1, ans2, ans3, cnt[M], tnc[M], nsum, musn;

int getrand()
{
	return seed = ((seed * 12321) ^ 9999) % 32768;
}
void generateData()
{
	std::cin >> k >> seed >> S;
	int t = 0, i;
	n = (k << 1) + 1;
	For (i, 1, n) t += (p[i] = getrand() / 128 & 1);
	i = 1;
	while (t > k)
	{
		while (!p[i]) i++;
		p[i] = 0; t--;
	}
	while (t < k)
	{
		while (p[i] == 1) i++;
		p[i] = 1; t++;
	}
}
int main()
{
	int i;
	generateData();
	For (i, 1, (k << 1) + 1) sum[i] = sum[i - 1] + (p[i] ? 1 : -1);
	For (i, 1, (k << 1) + 1) if (p[i])
		nsum += sum[i] > 0, cnt[sum[i] + E]++;
	For (i, 1, (k << 1) + 1)
	{
		if (p[i])
		{
			cnt[sum[i] + E]--;
			if (sum[i] > sum[i - 1]) nsum--;
		}
		if (!p[i]) nsum += cnt[sum[i - 1] + E];
		else nsum -= cnt[sum[i] + E];
		if (i > 1 && p[i - 1]) tnc[sum[i - 1] + E]++;
		if (!p[i]) musn += tnc[sum[i - 1] + 1 + E];
		else musn -= tnc[sum[i] + 1 + E];
		if (!p[i] && nsum + musn == 0) ans1 = i;
		if (!p[i] && nsum + musn == S) ans2 = i;
	}
	For (i, 1, (k << 1) + 1) sum[i] = sum[i - 1] + (p[i] ? -1 : 1);
	nsum = musn = 0;
	memset(cnt, 0, sizeof(cnt));
	memset(tnc, 0, sizeof(tnc));
	For (i, 1, (k << 1) + 1) if (!p[i])
		nsum += sum[i] > 0, cnt[sum[i] + E]++;
	For (i, 1, (k << 1) + 1)
	{
		if (!p[i])
		{
			cnt[sum[i] + E]--;
			if (sum[i] > sum[i - 1]) nsum--;
		}
		if (p[i]) nsum += cnt[sum[i - 1] + E];
		else nsum -= cnt[sum[i] + E];
		if (i > 1 && !p[i - 1]) tnc[sum[i - 1] + E]++, musn++;
		if (p[i]) musn += tnc[sum[i - 1] - 1 + E];
		else musn -= tnc[sum[i] - 1 + E];
		if (!p[i] && nsum + musn == S) ans3 = i;
	}
	std::cout << ans1 << std::endl << ans2 << std::endl << ans3 << std::endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/83096137