[题解] P10950 太鼓达人

P10950 太鼓达人

本题有搜索+判重+剪枝的做法,除了复杂度很玄学之外都比较简单,这里提供一种欧拉路解法。

首先,由一个子串转移到下一个子串,只需要在末尾加一位,然后把最高位删掉即可。

于是考虑图论建模,点用二进制表示每个长度为 \(k\) 的子串,边表示子串间的转移关系。

我们想让 \(m\) 最大,就必须经过所有点,点数是 \(2^k\),因此第一问的答案就是 \(2^k\)

然而,再往下做,我们发现这是个哈密顿回路,标准NP-Complete问题,那就相当于上面的所有过程全白费了,我们还是得爆搜。

只能转换思路。我们不妨把每个点上的数去掉最后一位,变成 \(k-1\) 位,然后把去掉的最后一位放到边上,这样,每一个子串都可以表示成“点+出边”的形式。现在我们只需要走完所有的边即可。

如下图:

iqwko0n8.png (388×205)

这就成了欧拉路了,不难发现欧拉路一定存在,于是直接跑板子,输出欧拉路的边上的数,结束。

真的结束了吗?

好像并没有,题目要求我们的构造方案要字典序最小,这涉及到起点的选择,如果从一个错误的起点开始跑,就会找到正确答案的循环同构串,不满足要求。

最简单粗暴的方法,就是随便跑出一个序列,然后按P13270 【模板】最小表示法的方法跑出最小表示串输出,但这样太麻烦了,不好用。

首先这个图一定有欧拉路,并且一定得是欧拉回路,那么我们从起点走一个圈又回到起点,不难发现起点状态会出现在最终输出串的末尾 \(k-1\)

为了让字典序最小,我们得把1更多的放到后面,也就是拿1最多的当起点。

于是这个题真的结束了......吗?

还没有。注意位运算操作的优先级问题。

#include<bits/stdc++.h>
using namespace std;

int del[(1<<12)];
int st[(1<<12)], buf[20];
int k,ans,tot;

void Dfs(int u){
	for(int i=del[u]; i<=1; i=del[u]){
		del[u]++;
		int v=((u<<1)|i) & ~(1<<(k-1));
		Dfs(v);
		st[++tot]=i;
	}
}
signed main(){
	cin.tie(0)->sync_with_stdio(0);
	cin>>k;
	cout<<(1<<k)<<" ";
	Dfs((1<<(k-1))-1);
	while(tot>0){
		cout<<st[tot];
		tot--;
	}
	return 0;
}

这次真的结束了

posted @ 2026-07-12 17:06  Cute_lxy  阅读(2)  评论(0)    收藏  举报