JZOJ3342. 【NOI2013模拟】求生之路

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/XLno_name/article/details/83091844

题意:

历经千辛万苦,pty终于打开了金字塔的锁。稍稍适应了外面刺眼的光线,pty抬头望去,眼前竟是一条不见尽头的狭长通道。这时候背后响起了奇怪的窸窣声,原来是金字塔内绿眼黑身的怪物追了过来。Pty来不及多想,便拼命往前奔去。通道狭窄又曲折,时不时还有断裂,不过Pty凭借TempleRun练成的娴熟技巧轻松通过。眼看着离怪物们越来越远时,一棵参天大树突然耸立在了道路中央,大树摆了摆身子,用苍老的声音说道:“孩子,我是远古的守护神。你打扰了这里的清净,想要从我这里通过,必须要解决一道来自远古的问题。”
现在有n堆石子,每堆石子分别有ai个,问有多少个d使得下式成立:
守护神出的题自然是神题了,同是身为神的你可以解决么?

数据范围:

对于30%的测试点, N < = 1 0 4 , a i < = 1 0 3 N<=10^4,a_i<=10^3
对于60%的测试点, N < = 1 0 4 , a i < = 1 0 6 N <= 10^4, a_i <= 10^6
对于100%的测试点, N < = 2 1 0 5 , a i < = 1 0 18 N <= 2*10^5, a_i <= 10^{18}

Analysis:

这种题肯定是拆位统计了。需要每一个二进制位上有偶数个1。那么DP,但是如果减去 d d 可能出现退位,非常不好处理。然后考虑设出退位状态,这时候发现如果我们从高位往低位DP。我们需要关注退了多少个1,但如果我们从低位到高位DP,根据当前退位状态,可以推出之后的状态,并不需要关心前面的状态。所以设 f i , j f_{i,j} 表示从低到高做到第 i i 位,退位状态为 j j 。但这样状态数,因为我们需要知道的是有多少个数退位了,若有 x x 个数退位,则肯定是按前 i i 个二进制位排序前 x x 小。因为越小才会退位,那么状态就会少,然后就可以转移了,排序用基数排序,就可以做到 O ( n log a i ) O(n\log{a_i}) 。注意到我们DP出来的是 < = d <=d 的方案,等于 d d 的时候要特判。

转移方程:
一开始我这里是最不理解的地方。
假如当前退位了 x x 个,因为 a i a_i 是确定下来的,考虑 d d 这一位选 0 0 还是选 1 1 去转移。
根据当前枚举到的位 a i a_i 排好序后统计出来的信息,我们可以根据是否能够向 i + 1 i+1 位借位讨论来转移。

Code:

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
ll f[70][N],a[N];
int s[N],t[N],p[N],b[N];
int n;
int main()
{
	scanf("%d",&n);
	f[0][0] = 1;
	for (int i = 1 ; i <= n ; ++i) scanf("%lld",&a[i]),p[i] = i;
	for (int i = 0 ; i < 62 ; ++i)
	{
		for (int j = 1 ; j <= n ; ++j) t[j] = s[j] = (a[p[j]] & (1ll << i)) ? 1 : 0;
		int h = 0;
		for (int j = 1 ; j <= n ; ++j) if (!t[j]) b[++h] = p[j];
		for (int j = 1 ; j <= n ; ++j) if (t[j]) b[++h] = p[j];
		for (int j = 1 ; j <= n ; ++j) s[j] += s[j - 1],p[j] = b[j];
		for (int j = 0 ; j <= n ; ++j)
		if (f[i][j])
		{
			if ((s[n] + j - s[j] * 2) % 2 == 0) f[i + 1][j - s[j]] += f[i][j];
			if ((2 * s[j] - j + n - s[n]) % 2 == 0) f[i + 1][n - s[n] + s[j]] += f[i][j];
		}
	}
	ll all = 0;
	for (int i = 2 ; i <= n ; ++i) all ^= a[p[i]] - a[p[1]];
	if (!all) --f[62][0];
	printf("%lld\n",f[62][0]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/XLno_name/article/details/83091844
今日推荐