P3694 邦邦的大合唱站队 解题报告

题意

\(N\) 个偶像排成一列,他们来自 \(M\) 个不同的乐队。每个团队至少有一个偶像。

现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。

请问最少让多少偶像出列?
对于全部数据,\(1\le N\le 10^5, M\le 20\)

思路

设计状态\(f_i\)代表状态为\(i\)的已完成队列,需要出队的最少人数。

举个例子,若\(f_i=011\),代表团队一和二站满了前排。

然后我们枚举每一个情况\(i\),对应的枚举所有的在\(i\)中的\(j\)(上面例子的话,\(j=1,2\)):

\[f[i]=min\{f[i\ xor \ 2^j]+num[j] -lenth[x,y][j]\} \]

\(num[j]\)代表团队\(j\)的总人数,\(lenth[x,y]\)代表在区间\([x,y]\)中团队\(j\)的总人数。

显然\([x,y]\)应该是接在状态\(i\ xor\ 2^j\)\(num[j]\)的那一段区间。

转移一下就好了,复杂度\(O(m2^m)\)

代码

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N = 2e6 + 5;
int n, m, x, dp[N], sum[100010][30], cnt[30], qwq[N]; 
void dfs2(int x, int sum);

bool check(int sum, int x)
{
	return (sum & (1 << (x - 1)));
}

void dfs1(int x, int sum)
{
	if (x == m)
	{
		return ;
	}
	qwq[sum | (1 << x)] = qwq[sum] + cnt[x + 1];
	dfs1(x + 1, sum | (1 << x));
	dfs2(x + 1, sum | (1 << x));
}

void dfs2(int x, int sum)
{
	if (x == m)
	{
		return ;
	}
	dfs1(x + 1, sum);
	dfs2(x + 1, sum);	
}

signed main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i ++)
	{
		cin >> x; 
		for (int j = 1; j <= m; j ++)
		{
			sum[i][j] = sum[i - 1][j];
		}
		sum[i][x] ++;
		cnt[x] ++;
	}
	dfs2(0, 0);
	dfs1(0, 0);
	memset(dp, 0x3f, sizeof(dp));
	dp[0] = 0;
	for (int i = 1; i < (1 << m); i ++)
	{
		for (int j = 1; j <= m; j ++)
		{
			if (check(i,j))
			{
				int l = qwq[i ^ (1 << j - 1)];
				dp[i] = min(dp[i], dp[i ^ (1 << (j - 1))] + (qwq[i] - l) - (sum[qwq[i]][j] - sum[l][j]));
			}	
		}
	}
	cout << dp[(1 << m) - 1] << endl;
}
posted @ 2026-06-05 13:40  Luowj  阅读(8)  评论(0)    收藏  举报