Codeforces Global Round 17 - D. Not Quite Lee

裴蜀定理 + lowbit

Problem - D - Codeforces

题意

定义一个包含 \(m\) 个元素的数组 \(b\) 是好的,当且仅当满足以下两个条件

  1. 对于 \(b[i]\), 存在一个长度为 \(b[i]\) 的连续的段(如 \(b[i]=4\), [1,2,3,4], [-1,0,1,2]等就是符合条件的)
  2. 对于这 \(m\) 个段,\(sum_i\) 记为这一段的数字和,并满足 \(\sum sum_i=0\)]

给定一个长度为 \(n\;(1<=n<=2*10^5)\) 的数组 \(a[i]\),求 \(a\)\(2^n-1\) 个非空子序列组成的数组中,有多少个是好的

思路

  1. 对于 \(a[i]\) 而言,设选取的一段的第一个数是 \(x_i\), 则 \(sum_i=a_i*x_i+\frac {a_i*(a_i-1)}2\)

  2. 若选了 \(a\) 数组的 \(k\) 个数,记为 \(c_1,c_2...c_k\)

    \(\sum sum_i=0\Lrarr \sum c_i*x_i=\sum \frac {c_i*(c_i-1)}2\)

​ 根据裴蜀定理,设 \(g=\gcd(c_1,c_2,...,c_k)\), 则要满足 \(g\mid \sum \frac {c_i*(c_i-1)}2\)

  1. 如果 \(c_i\) 中存在奇数,则 \(g\) 也一定是奇数,所以右边的除以2不影响整除的性质,又因为 \(g\mid c_i\), 因此 \(g\mid \sum \frac {c_i*(c_i-1)}2\) 恒成立

    所以奇数部分对方案数的贡献就是 \(2^{odd}\) (也可以感性考虑,如果存在一个奇数,那么它和其余所有的偶数共有奇数个数,把它们按关于 0 对称排列即可;其余的奇数也关于 0 对称排列)

  2. 现在只关心偶数部分,因为 \(g\mid c_i\), 问题是右边的除以 2 之后还能否满足 \(g\mid \sum \frac {c_i*(c_i-1)}2\),(即关心的是 \(c_i\) 的含 2 量) 由于 \(c_i-1\) 为奇数,对除以 2 而言没有意义,不用考虑;

  3. 现在转化为 \(g\mid \sum \frac {c_i}2\) 是否成立,记 \(lowb[x]\) 为能整除 \(x\) 的最高的 2 的幂次,例如 \(lowb[8]=3,lowb[16]=4\)

    \(lowb[g]<=lowb[c_i]\)

    1. \(lowb[c_i]>lowb[g]\), 那么 \(\frac {c_i}2\) 仍能被 \(g\) 整除
    2. \(lowb[c_i]==lowb[g]\), 需要偶数个相等的 \(c_i\) 才能使 \(g\mid \sum \frac {c_i}2\) (需要用到 \(\binom n0+\binom n2+\binom n4+...=2^{n-1}\) 来优化)

    代码

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;

const int N = 2e5 + 10;
const int mod = 1e9 + 7;
int n;
int a[N];
int lowb[40];
ll mi[N];

void add(ll &a, ll b)
{
	a += b;
	if (a >= mod)
		a -= mod;
}

void presolve()
{
	mi[0] = 1;
	for (int i = 1; i <= n; i++)
		mi[i] = mi[i-1] * 2 % mod;
	for (int i = 1; i <= n; i++)
	{
		int x = a[i], cnt = 0;
		while(x % 2 == 0)
		{
			x /= 2;
			cnt++;
		}
		lowb[cnt]++;
	}
}

ll solve()
{
	ll ans = 0;
	int even = 0;
	for (int i = 1; i <= n; i++)
		if (a[i] % 2 == 0) even++;
    //至少一个奇数 + 任意个偶数
	add(ans, mi[even] * (mi[n - even] - 1 + mod) % mod);
    //只有偶数
	for (int i = 1; i <= 30; i++)
	{
		if (!lowb[i])
			continue;
		even -= lowb[i];
		//枚举拿的最小的lowbit,且要拿偶数个,C(n,0) + C(n,2) + C(n,4) + ... == 2^(n-1),且不能拿0个
		add(ans, mi[even] * (mi[lowb[i] - 1] - 1 + mod) % mod);
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	presolve();
	cout << solve() << endl;
    return 0;
}
posted @ 2022-10-18 21:32  hzy0227  阅读(28)  评论(0编辑  收藏  举报