[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[i]={11iPi/P
然后设 a a a 的前缀和:
s u m [ i ] = s u m [ i − 1 ] + a [ i ] sum[i]=sum[i-1]+a[i] sum[i]=sum[i1]+a[i]
设起点为 x x x a [ x ] = − 1 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] i=x+12k+1[sum[i]sum[x]>0]+i=1x1[1sum[x]+sum[i]>0]
方括号里的东西可以表示成:
s u m [ i ] > s u m [ x ] sum[i]>sum[x] sum[i]>sum[x]
s u m [ i ] > s u m [ x ] + 1 sum[i]>sum[x]+1 sum[i]>sum[x]+1
于是我们用树状数组可以做到 O ( k log ⁡ k ) O(k\log k) O(klogk) 的复杂度。
k k k 1 0 7 10^7 107 级别,我们需要想办法优化。
发现 s u m [ i ] sum[i] sum[i] 等于 s u m [ i − 1 ] + 1 sum[i-1]+1 sum[i1]+1 s u m [ i − 1 ] − 1 sum[i-1]-1 sum[i1]1
所以只需要维护一个指针 p p p ,每次查询时根据 a [ i ] a[i] a[i] 的值将指针左移或右移,同时维护 i i i 的右边有多少个 s u m > p sum>p sum>p 及左边有多少个数大于 p + 1 p+1 p+1 。但同时 i i i 的值会变,所以 s u m sum sum 的查询范围也会变(相当于插入 / 删除 一个数),根据与 p p p p + 1 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[i]={11iPi/P ,找答案时找 a [ x ] = 1 a[x]=1 a[x]=1 的位置 x x 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] i=x+12k+1[sum[i]sum[x]>0]+i=1x1[1sum[x]+sum[i]>0]
复杂度 O ( k ) 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值