给出一个序列的数字, a i a_i ai表示要选择一串 a i a_i ai个连续数字。问有多少个子序列,满足这些连续数字和为0。
从等差数列求和来考虑。长度 a i a_i ai是给定的,但开始位置是待定的,记为 s i s_i si。
这一串数字的和为 s i ∗ a i + 1 2 ( a i − 1 ) ∗ a i s_i*a_i + \frac12 (a_i-1)*a_i si∗ai+21(ai−1)∗ai,可以看作 a i ∗ ( s i + 1 2 a i − 1 2 ) a_i*(s_i + \frac12 a_i - \frac12) ai∗(si+21ai−21)。
由于 s i s_i si的任意性,不妨设 k ∈ Z k\in \mathbb{Z} k∈Z,
对于 a i a_i ai是奇数,可以化为 a i ∗ k a_i * k ai∗k。
对于 a i a_i ai是偶数,只能化成 a i ∗ ( k + 1 2 ) a_i * (k + \frac12) ai∗(k+21)。
目标是子序列的数和为 0 0 0。
可以发现,只要出现了一个奇数,这个子序列一定是满足条件的。
所有问题可以拆分成:存在奇数的子序列,纯偶数的子序列。
前者是好解决的, ( 2 奇数个数 − 1 ) ∗ 2 偶数个数 (2^{奇数个数} - 1 ) * 2^{偶数个数} (2奇数个数−1)∗2偶数个数。减一是除去了一个奇数不选的情况。
后者也可以解决。先确定一个最小偶数为 a i a_i ai后,那剩的一个 1 2 \frac12 21的消除方式只能让偶数个这样的数加起来。
这样的方案数是 ( n 2 ) + ( n 4 ) + ⋯ + ( n ⌊ n / 2 ⌋ ∗ 2 ) \binom{n}{2} + \binom{n}{4} + \cdots + \binom{n}{\lfloor n/2\rfloor *2} (2n)+(4n)+⋯+(⌊n/2⌋∗2n),也就是偶数项相加,无论 n n n是奇还是偶,和是固定的 2 n − 1 − 1 2^{n - 1} - 1 2n−1−1。减一是去掉了 ( n 0 ) \binom{n}{0} (0n)。
所有相加即可。
我也想到了纯偶数情况下和为 ( 1 2 + k ) a i (\frac12 + k)a_i (21+k)ai,想着找到 ∑ 1 2 a i = 0 \sum \frac12 a_i = 0 ∑21ai=0的情况。这东西很难求,错就错在没有划分讨论。先确定最小偶数为xx,那方案数就可以逐步求解了。
#include
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int N = 2e5 + 10;
ll qkpow(ll a, ll n)
{
ll ret = 1;
while(n)
{
if(n & 1) ret = ret * a % mod;
a = a * a % mod;
n >>= 1;
}
return ret;
}
ll bin[N], fac[N], ifac[N];
void init()
{
bin[0] = 1;
for(int i = 1; i < N; i++) bin[i] = (bin[i - 1] << 1) % mod;
fac[0] = 1;
for(int i = 1; i < N; i++) fac[i] = fac[i - 1] * i % mod;
ifac[N - 1] = qkpow(fac[N - 1], mod - 2);
for(int i = N - 2; i >= 0; i--) ifac[i] = ifac[i + 1] * (i + 1);
}
int count(int x)
{
int ret = 0;
while((x & 1) == 0 && x) ret++, x >>= 1; // debug x & 1 == 0 (x)
return ret;
}
int n, a[N], cnt[100];
void solve()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
cnt[count(a[i])]++;
}
int rest= n - cnt[0];
ll ans = (bin[cnt[0]] - 1) * bin[rest] % mod;
for(int i = 1; i <= 35; i++)
{
if(!cnt[i]) continue; // debug
rest -= cnt[i];
ll tmp = (bin[cnt[i] - 1] - 1) * bin[rest] % mod;
ans = (ans + tmp) % mod;
}
printf("%lld\n", ans);
}
signed main()
{
int T = 1;
// scanf("%d", &T);
init();
while(T--) solve();
}