题解 书

传送门

哇啊啊兔兔你为什么不对我们好一点呜呜呜

做了三个小时,最后十分钟胡出来一个基本思路正确的做法
那有什么用,不还是垫底了

首先发现各种做法全是 \(2^n\) 的,要么需要记用了哪些数要么需要记填了哪些位置
发现书签形成的连续段可以缩减点数
如果只考虑形成的连续段的话这东西最多有 \(\frac{n}{2}\)
那么尝试把思路往这边搞
发现状态里不能记不在连续段里的点,所以容斥那部分的点
\(2^{cnt}\) 枚举肯定还是不行,但是发现连续段的顺序可以任意打乱
那么只需要考虑所有本质不同的且合法(能把 1-2-4 这些链填进去)的连续段方案
赛时想到这里了,但跑去算复杂度的时候忘了只要考虑本质不同方案这个事了所以……
本质不同的方案是容易搜出来的
然后因为链真的很少所以直接拿 vector 记状态 DP 对应的合法的 b 数组的方案数是可以的
然后同样拿 vector 存状态可以记搜得到将这些链填进去的方案数

然后正解:
谁说状压一定要 \(2^n\) 了?
发现长度相等的链可以合为一类
发现长度只有 6 种
发现最多只有 12 条链
开 vector,记状态里就行了
还要记录上个点选在哪种链的什么位置
一个复杂度上界是 \(O(40^6*\sum\limits_{i=1}^6i^2)\),但上界的意思就是前面那个 \(6\) 次方的东西连百分之一都跑不到

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define ll long long
//#define int long long

int n;
char b[N], c[N];
const ll mod=1e9+7;
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}

namespace force{
	ll f[1<<20][21], ans;
	void solve() {
		int lim=1<<n;
		f[0][0]=1;
		for (int s=0; s<lim; ++s) {
			int id=__builtin_popcount(s);
			for (int i=0; i<=n; ++i) if (f[s][i]) {
				for (int j=1; j<=n; ++j) if (!(s&(1<<j-1))) {
					if (b[id]=='1' && i!=j*2 && j!=i*2) continue;
					if (b[id]=='0' && (i==j*2||j==i*2)) continue;
					md(f[s|(1<<j-1)][j], f[s][i]);
				}
			}
		}
		for (int i=1; i<=n; ++i) md(ans, f[lim-1][i]);
		cout<<ans<<endl;
	}
}

namespace task{
	ll ans;
	bool vis[N];
	map<pair<vector<int>, pair<int, int>>, ll> mp[45];
	void solve() {
		vector<int> base(7);
		for (int i=1; i<=n; ++i) if (!vis[i]) {
			int cnt=0;
			for (int t=i; t<=n; t<<=1) vis[t]=1, ++cnt;
			++base[cnt];
		}
		for (int i=1; i<=6; ++i) if (base[i])
			for (int j=1; j<=i; ++j)
				mp[2][{base, {i, j}}]=base[i];
		for (int i=2; i<=n; ++i) {
			// cout<<"i: "<<i<<endl;
			for (auto it:mp[i]) {
				// cout<<"it: "; for (int i=1; i<=6; ++i) cout<<it.fir.fir[i]<<' '; cout<<"{"<<it.fir.sec.fir<<','<<it.fir.sec.sec<<"}"; cout<<endl;
				vector<int> vec=it.fir.fir;
				pair<int, int> s=it.fir.sec;
				if (b[i-1]=='1') {
					vector<int> tem=vec;
					--tem[s.fir];
					if (s.sec-1) ++tem[s.sec-1];
					if (s.fir-s.sec) ++tem[s.fir-s.sec];
					if (s.sec-1) md(mp[i+1][{tem, {s.sec-1, s.sec-1}}], it.sec);
					if (s.fir-s.sec) md(mp[i+1][{tem, {s.fir-s.sec, 1}}], it.sec);
				}
				else {
					vector<int> tem=vec;
					--tem[s.fir]; --vec[s.fir];
					if (s.sec-1) ++vec[s.sec-1];
					if (s.fir-s.sec) ++vec[s.fir-s.sec];
					for (int j=1; j<=6; ++j) if (tem[j]) {
						for (int k=1; k<=j; ++k)
							md(mp[i+1][{vec, {j, k}}], it.sec*tem[j]%mod);
					}
					if (s.sec-1) {
						for (int j=1; j<s.sec-1; ++j)
							md(mp[i+1][{vec, {s.sec-1, j}}], it.sec);
					}
					if (s.fir-s.sec) {
						for (int j=2; j<=s.fir-s.sec; ++j)
							md(mp[i+1][{vec, {s.fir-s.sec, j}}], it.sec);
					}
				}
			}
		}
		for (auto it:mp[n+1]) md(ans, it.sec);
		cout<<ans<<endl;
	}
}

signed main()
{
	freopen("book.in", "r", stdin);
	freopen("book.out", "w", stdout);

	scanf("%d%s", &n, b+1);
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2022-06-04 21:01  Administrator-09  阅读(3)  评论(0编辑  收藏  举报