• CF比赛1610D. Not Quite Lee(Codeforces Global Round 17)(数学)(计数)


    给出一个序列的数字, 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 siai+21(ai1)ai,可以看作 a i ∗ ( s i + 1 2 a i − 1 2 ) a_i*(s_i + \frac12 a_i - \frac12) ai(si+21ai21)

    由于 s i s_i si的任意性,不妨设 k ∈ Z k\in \mathbb{Z} kZ

    对于 a i a_i ai是奇数,可以化为 a i ∗ k a_i * k aik

    对于 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/22n),也就是偶数项相加,无论 n n n是奇还是偶,和是固定的 2 n − 1 − 1 2^{n - 1} - 1 2n11。减一是去掉了 ( 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();
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
  • 相关阅读:
    Centos7卸载minio
    MP4视频播放时绿屏|屏幕变成绿色| AVC编码完美解决方案
    《C++API设计》读书笔记(3):模式
    简单ELK配置实现生产级别的日志采集和查询实践
    Javaweb安全——JSP Webshell
    scanf(“%s“, filename);这里的scanf函数中,“,”逗号符号后面什么时候需要用“&”这个符号,什么时候不需要用这个“&”符号?
    Docker安装MariaDB
    Doris表的动态分区
    C语言练习百题之排序算法
    react native使用5-搭建ios环境
  • 原文地址:https://blog.csdn.net/A_Bright_CH/article/details/127442461