题解 [APIO2022] 排列

传送门

具体做法见 @WeLikeStudying 的题解
核心思路是首先易得 $\log^2 $ 做法
发现有共用点可以做到 \(\log n+\mathrm{popcount}\)
考虑题解的优化
若二进制位中有两个连续的 1,那么让低位的 1 被算 3 次是等价的
题解在做的就是这样一个事情
那么复杂度就是 \(O(\log n+\frac{\mathrm{popcount}}{2})=O(\frac{3}{2}\log n)\)

点击查看代码
#include "perm.h"
#include <bits/stdc++.h>
using namespace std;
#define fir first
#define sec second
#define ll long long
#define pb push_back

vector<int> force_construct_permutation(ll k) {
	vector<int> ans;
	// cout<<bitset<10>(k)<<endl;
	vector<vector<int>> tem; tem.resize(200);
	int n=63-__builtin_clzll(k), pos=0, tot=0;
	for (int i=0; i<n; ++i) tem[i].pb(pos++);
	for (int i=n-1; ~i; --i) if (k&(1ll<<i)) tem[i].pb(pos++);
	ans.resize(pos);
	for (int i=0; i<n; ++i) {
		reverse(tem[i].begin(), tem[i].end());
		for (auto it:tem[i]) ans[it]=tot++;
	}
	// cout<<"ans: "; for (auto it:ans) cout<<it<<' '; cout<<endl;
	return ans;
}

vector<int> construct_permutation(ll k) {
	if (__builtin_popcount(k)<=2) return force_construct_permutation(k);
	vector<int> ans, sec;
	// cout<<bitset<10>(k)<<endl;
	vector<vector<int>> fir, tem;
	fir.resize(200); tem.resize(200);
	int n=63-__builtin_clzll(k), pos=0, cnt=0, tot=0;
	for (int i=0; i<n; ++i) fir[i].pb(pos++);
	for (int i=n-1; i>=0; --i) if (k&(1ll<<i)) {
		if (i && k&(1ll<<i-1)) {
			if (sec.size()>=2) sec.insert(sec.end()-2, --i);
			else sec.pb(i);
		}
		else sec.pb(i);
	}
	for (auto it:sec) tem[it].pb(pos++);
	ans.resize(pos);
	for (int i=0; i<n; ++i) {
		for (auto it:tem[i]) ans[it]=tot++;
		for (auto it:fir[i]) ans[it]=tot++;
	}
	// cout<<"ans: "; for (auto it:ans) cout<<it<<' '; cout<<endl;
	return ans;
}
posted @ 2022-06-10 18:01  Administrator-09  阅读(4)  评论(0编辑  收藏  举报