2024/7/2 T1

小超手123發表於2024-07-02

題意:

分析:

\(S_{i}\) 表示目前第 \(i\) 個集合裡的元素個數。

集合之間互不區分,強制欽定必須滿足 \(S_{i} \le S_{i+1}(i<k)\)

經搜尋發現,這樣的狀態數量最多約為 \(1.8 \times 10^5\)

極差可以這樣處理:將 \(a\) 排序,\(S_{i}\) 第一次加入某個元素 \(x\),則貢獻加上 \(-x\)\(S_{i}\)\(y\) 填滿,則貢獻加上 \(+y\)

這樣看起來可以 dp,但還有一個問題:如何處理每個集合的元素必須互不相同呢?

可以強制要求所有相同的元素必須以一個固定的順序加入,使得其在不同的集合,可以從大到小加或從小到大加(元素個數)。這裡採用前者。

f[S][j]表示(從小往大填)已經填了i(∑S[i])個數,第i個數在加入後它目前在的集合的大小為j
當我們填入a[i+1]時,如果a[i+1]=a[i],則需要保證a[i+1]填入的集合的大小小於j即可 

然後做完了!

時隔 \(7\) 個月,以前的我看不懂題解,但現在我 AC 了,牛不牛。

#include<bits/stdc++.h>
#define N 110
#define int long long
#define base 131
using namespace std;
bool s1;

int n, k, len;
int a[N], now[N];

vector<int>z[110];
int S[200005][110];
int tot;

unordered_map<int, int>id;

void dfs(int x, int lst) {
	if(x > k) {
		tot++;
		int g = 0;
		int num = 0;
		for(int i = 1; i <= k; i++) {
			S[tot][i] = now[i];
			g += now[i];
			num = num * base + now[i];
		}
		id[num] = tot;
		z[g].push_back(tot);
		//cout << tot << endl;
		return;
	}
	for(int i = lst; i <= len; i++) {
		now[x] = i;
		dfs(x + 1, i);
	}
}

int f[200005][110];
//f[S][j]表示(從小往大填)已經填了i(∑S[i])個數,第i個數在加入後它目前在的集合的大小為j
//當我們填入a[i+1]時,如果a[i+1]=a[i],則需要保證a[i+1]填入的集合的大小小於j即可 

void upd(int &x, int y) {
	x = min(x, y);	
}

bool s2;
signed main() {
	//freopen("diyiti06.in", "r", stdin);
	//ios::sync_with_stdio(0);
	//cin.tie(0); cout.tie(0);
	//cerr << (&s1 - &s2) / 1024 / 1024 << " MB\n";
	cin >> n >> k;
	len = n / k;
	for(int i = 1; i <= n; i++) cin >> a[i];
	/*if(k == n) {
		cout << 0;
		return 0;
	}*/ 
	sort(a + 1, a + n + 1);
	dfs(1, 0);
	//cerr << "e";
	for(int S = 1; S <= tot; S++)
	for(int j = 0; j <= len; j++) f[S][j] = 1e18;
	
	/*for(int s = 1; s <= tot; s++) {
		cout << "S " << s << " : ";
		for(int j = 1; j <= len; j++) cout << S[s][j] << " ";
		cout << endl;
	}
	
	for(int i = 1; i <= n; i++) {
		cout << i << " : ";
		for(auto x : z[i]) cout << x << " ";
		cout << endl; 
	}*/
	
	f[2][1] = -a[1];
	if(len == 1) f[2][1] = 0;
	for(int i = 1; i < n; i++) {
		for(auto s : z[i]) {
			//if(i == 1) cout << "look : " << s << endl;
			for(int x = 1; x <= k; x++) {
				if(S[s][x]) {
					//if(i == 1) cout << "ppp : " << x << endl;
					int j = S[s][x]; //第i個數加入的是第x個集合 
					if(f[s][j] == 1e18) continue;
					
					//printf("f[%lld][%lld] = %lld\n", s, j, f[s][j]);
					
					//考慮填入a[i+1]
					for(int y = 1; y <= k; y++) { //將a[i+1]放入第y個集合裡 
						if(a[i] == a[i + 1] && S[s][y] >= j) break;
						for(int h = 1; h <= k; h++) now[h] = S[s][h];
						now[y]++;
						if(now[y] > len) continue;
						int J = now[y], derta = 0;
						if(J == len) derta += a[i + 1];
						if(J == 1) derta -= a[i + 1];
						sort(now + 1, now + k + 1);
						int num = 0;
						for(int h = 1; h <= k; h++) 
							num = num * base + now[h];
						int Nxt_S = id[num];
						upd(f[Nxt_S][J], f[s][j] + derta);	
					}
					
				}	
				
			}
		}
	}
	
	if(f[tot][len] == 1e18) cout << -1;
	else cout << f[tot][len];
	return 0;
}

/*
6 3
1 1 1 1 2 5
*/