1 条题解

  • 0
    @ 2026-5-6 15:52:46

    不考虑分数相同的情况,这种特殊处理一下即可。

    降分数从小到大排序,每个人可能的分数是一个区间,合法当且仅当每个区间的上界都小于下一个区间的下界。

    考虑一个人前一个人和后一个人的真实分数的差 dd,如果 d<kd<k,那么当前这个人的分数必须全部公开,否则这个人的分数一定是长为 kk 的区间,必然与前后至少一个有交。在这个结论的基础上不难发现,这个人最多不公开 dk\left\lfloor\frac{d}{k}\right\rfloor 个分数。所以所有人可以公开的分数总数是 O(m)O(m) 的而不是 O(nm)O(nm)

    然后就可以直接 dp 了,设 fi,jf_{i,j} 表示考虑到第 ii 个人,这个人分数的上界是 jj,最少公开多少个分数。转移的时候对当前人求一个背包,gi,jg_{i,j} 表示当前人有 ii 个不公开,公开的分数总和(下界)是 jj 是否可行。这个背包可以用 bitset 优化。精细实现可以做到 O(m3kw+nlogn)O(\frac{m^3k}{w}+n\log n)。下面代码是 O(nlogn+nmk+m3k)O(n\log n+nmk+m^3k)

    #include <bits/stdc++.h>
    using namespace std;
    
    namespace z {
    
    const int N = 2e4 + 5, inf = 1e9;
    struct o {
    	int a[105];
    	int sum, id;
    	bool operator < (const o &b) const {
    		if(sum == b.sum) return id > b.id;
    		return sum < b.sum;
    	}
    	int& operator [] (int x) {
    		return a[x];
    	}
    } a[N];
    int f[2][10005];
    bool g[205][10005];
    bool h[205][10005];
    void main() {
    
    	ios::sync_with_stdio(false);
    	cin.tie(nullptr);cout.tie(nullptr);
    	int n, m, k; cin >> n >> m >> k;
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++) {
    			cin >> a[i][j];
    			a[i].id = i;
    			a[i].sum += a[i][j];
    		}
    	sort(a + 1, a + n + 1);
    	memset(f, 0x3f, sizeof(f));
    	f[0][0] = 0; 
    	for(int i = 1; i <= n; i++) {
    		for(int j = 1; j <= m * k; j++) f[i - 1 & 1][j] = min(f[i - 1 & 1][j], f[i - 1 & 1][j - 1]);
    		memset(f[i & 1], 0x3f, sizeof(f[i & 1]));
    		int lim = min(m, (i == 1 || i == n ? m : (a[i + 1].sum - a[i - 1].sum) / k));
    		if(lim) {
    			memset(g, 0, sizeof(g));
    			g[0][0] = 1;
    			for(int j = 1; j <= m; j++) {
    				for(int l = 0; l <= lim; l++) memset(h[l], 0, sizeof(h[l]));
    				for(int l = 0; l <= lim; l++) {
    					for(int s = 0; s <= m * k; s++) {
    						if(l) h[l][s] |= g[l - 1][s];
    						if(s >= a[i][j]) h[l][s] |= g[l][s - a[i][j]];
    					}
    				}
    				for(int l = 0; l <= lim; l++) memcpy(g[l], h[l], sizeof(h[l]));
    			}
    			for(int l = 0; l <= lim; l++) 
    				for(int s = 0; s <= m * k; s++) if(g[l][s] && l * k + s <= m * k) {
    					int t = s - (i == 1 ? 0 : (a[i].id > a[i - 1].id));
    					if(t >= 0) f[i & 1][l * k + s] = min(f[i & 1][l * k + s], f[i - 1 & 1][t] + m - l);
    				}
    		} else {
    			int s = accumulate(a[i].a + 1, a[i].a + m + 1, 0);
    			f[i & 1][s] = f[i - 1 & 1][s - (i == 1 ? 0 : (a[i].id > a[i - 1].id))] + m;
    		}
    	}
    	int ans = 1e9;
    	for(int i = 0; i <= m * k; i++) ans = min(ans, f[n & 1][i]);
    	cout << ans << '\n';
    }
    
    #undef int
    
    }
    
    
    int main()
    {
    	z::main();
    	return 0;
    }
    
    • 1

    信息

    ID
    10188
    时间
    6000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者