1 条题解

  • 1
    @ 2023-8-24 9:58:50

    思路

    题解区的题解都多多少少有些错误。

    我想写一写我的做法,是将题解区各大佬的做法综合起来的做法。


    首先,假如每一个数字都比前面的一个数字大 11,即数列为 0,1,2,3,4,5,,n10, 1, 2, 3, 4, 5, \dots, n - 1,那么这个数列的和为 sum=n(n1)2sum = \frac{n(n - 1)}{2},我们发现 n100n \le 100,那么 sum4950sum \le 4950,所以如果题目要求的 s>sums > sum,那么一定无解,因为这个数列再大也大不过 sumsum,更不可能到达 ss 了。

    反过来,如果一个数字比前面一个数字小 11,即数列为 0,1,2,3,4,5,,(n1)0, -1, -2, -3, -4, -5, \dots, -(n - 1),那么这个数列的和为 sum=n(n1)2sum = -\frac{n(n - 1)}{2},我们发现 n100n \le 100,那么 sum4950sum \ge -4950

    所以 263s263-2^{63} \le s \le 2^{63} 是吓你的,真正有用的 4950s4950-4950 \le s \le 4950


    接下来,我们来探究一个位子 uu 上的数字变化对数组的和 sumsum 的影响。

    如果 au=au1+1a_u = a_{u - 1} + 1,现在改成 au=au11a_{u} = a_{u - 1} - 1,那么从 uu 开始的每一个数字都会减去 22

    image

    那么数组的和 sumsum 就会减去 2(nu+1)2(n - u + 1)


    那么,我们可以想到让每一个数字都等于前面一个数字 +1+1,那么和就是 n(n1)2\frac{n(n - 1)}{2}

    但是我们想让其变为题目要求的 ss,那么要减去的数字就是 k=n(n1)2sk = \frac{n(n - 1)}{2} - s

    那么我们只能将有些数字间的 +1+1,换成 1-1 才能达到减去 kk 的目的。

    那么根据前面对数字间关系的讨论,我们知道,我们要凑出很多个 ii,使这些 ii 对应的 2(ni+1)2(n - i + 1) 加起来等于 kk 就行了,即让所有的 ni+1n - i + 1 加起来等于 k2\frac{k}{2} 即可。

    当然了,如果 kk 是奇数,那么一定无解。


    我们考虑 DP,设 fi,jf_{i, j} 表示已经凑到第 ii 个数字,和为 jj 的方案数。

    开始时:f1,0=1f_{1, 0} = 1

    目标:fn,k2f_{n, \frac{k}{2}}

    我们可以不选择第 ii 个数字,fi,j=fi1,jf_{i, j} = f_{i - 1, j}

    我们可以选择第 ii 个数字,即在原有基础上加上 (ni+1)(n - i + 1)fi,j=fi1,j(ni+1)f_{i, j} = f_{i - 1, j - (n - i + 1)}


    然后爆搜 + 剪枝输出方案就可以了,因为最多输出 100100 项,所以不用担心会不会超时,当然不剪枝是过不了的。

    (相信大家都会)


    代码

    注意取模!!!

    我用了 __int128

    // Problem: P1329 数列
    // Contest: Luogu
    // URL: https://www.luogu.com.cn/problem/P1329
    // Memory Limit: 128 MB
    // Time Limit: 1000 ms
    
    
    #include <bits/stdc++.h>
    
    using namespace std;
    using i64 = long long;
    using ull = unsigned long long;
    
    const int N = 1010, M = 5010;
    const __int128 mod = ((__int128)1 << 64);
    
    ull f[N][M];
    
    int n, k;
    i64 s;
    int cnt;
    int m[N];
    
    void dfs(int u, int sum) {
        if (sum > (k >> 1)) return;
    	if (u > n) {
    		if (sum == (k >> 1)) {
    			cnt++;
    			i64 tmp = 0;
    			for (int i = 1; i <= n; i++) {
    				tmp += m[i];
    				cout << tmp << ' ';
    			}
    			cout << '\n';
    		}
    		if (cnt >= 100) {
    			exit(0);
    		}
    		return;
    	}
    	m[u] = -1;
    	dfs(u + 1, sum + (n - u + 1));
    	m[u] = 1;
    	dfs(u + 1, sum);
    }
    
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(nullptr);
    	
    	cin >> n >> s;
    	if (s > n * (n - 1) / 2) {
    		cout << 0 << '\n';
    		return 0;
    	}
    	k = n * (n - 1) / 2 - s;
    	if (k & 1) {
    	    cout << 0 << '\n';
    	    return 0;
    	}
    	f[1][0] = 1;
    	for (i64 i = 2; i <= n; i++) {
    		i64 x = (n - i + 1);
    		memcpy(f[i], f[i - 1], sizeof(f[i]));
    		for (int j = x; j < M; j++) {
    			f[i][j] = ((__int128)f[i][j] + f[i - 1][j - x]) % mod;
    		}
    	}
    	cout << f[n][k >> 1] << '\n';
    	dfs(2, 0);
    	return 0;
    }
    
    • 1

    信息

    ID
    330
    时间
    1000ms
    内存
    128MiB
    难度
    4
    标签
    递交数
    1
    已通过
    1
    上传者