康托展开:已知一个排列,求这个排列在全排列中是第几个
ll kt() { // 计算 p序列在全排列排第几
ll ret = 0;
for(int i=1; i<=n; i++) {
ll tmp = 0;
for(int j=i+1; j<=n; j++)
if(p[j] < p[i]) tmp++;
ret += tmp * fac[n-i]; //f[]为阶乘
}
return ret;
}
康托展开逆运算:已知在全排列中排第几,求这个排列
void kt(ll x, int n) { // 求第 x的排列,返回为 p
memset(used, 0, sizeof(used));
for(rint i=1; i<=n; i++) {
int t = x/fac[n-i]+1, k = 0;
for(rint j=1; j<=n; j++)
if(!used[j] && ++k==t) {
k = j;
break;
}
used[p[i]=k] = 1;
x %= fac[n-i];
}
}
例题链接:点我啊╭(╯^╰)╮
题目大意:
长度为
n
n
n 的排列
a
a
a ,有两种操作
1
1
1
l
l
l
r
r
r ,求
∑
i
=
l
r
a
i
\sum_{i=l}^{r}a_i
∑i=lrai
2
2
2
x
x
x, 对
a
a
a 执行
x
x
x 次下一个排列
解题思路:
发现
q
×
x
≤
2
e
10
q \times x ≤ 2e10
q×x≤2e10,因此只会对末尾
15
15
15 个数字进行修改
用逆康拓维护暴力修改末尾
15
15
15 个数字,然后前缀和维护即可
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 1e6 + 5;
int n, q, a[maxn];
int p[20], used[20];
ll fac[20], pre[maxn];
ll kt() { // 计算 p序列在全排列排第几
ll ret = 0;
for(int i=1; i<=n; i++) {
ll tmp = 0;
for(int j=i+1; j<=n; j++)
if(p[j] < p[i]) tmp++;
ret += tmp * fac[n-i]; //f[]为阶乘
}
return ret;
}
void kt(ll x, int n) { // 求第 x的排列,返回为 p
memset(used, 0, sizeof(used));
for(rint i=1; i<=n; i++) {
int t = x/fac[n-i]+1, k = 0;
for(rint j=1; j<=n; j++)
if(!used[j] && ++k==t) {
k = j;
break;
}
used[p[i]=k] = 1;
x %= fac[n-i];
}
}
int main() {
fac[0] = 1;
for(int i=1; i<=15; i++) fac[i] = fac[i-1] * i;
scanf("%d%d", &n, &q);
for(int i=1; i<=n; i++) a[i] = i, pre[i] = pre[i-1] + a[i];
ll cnt = 0; kt(cnt, 15);
while(q--) {
int op, l, r, x;
scanf("%d", &op);
if(op == 1) {
scanf("%d%d", &l, &r);
printf("%lld\n", pre[r] - pre[l-1]);
} else {
scanf("%d", &x);
cnt += x, kt(cnt, 15);
if(n > 15) for(int i=n-14; i<=n; i++)
a[i] = p[i-n+15] + n - 15;
else for(int i=1; i<=n; i++) a[i] = p[i+15-n] - 15 + n;
for(int i=max(1, n-14); i<=n; i++) pre[i] = pre[i-1] + a[i];
}
}
}