题解 [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;
}