当前位置: 首页 > news >正文

康拓展开补充:逆康拓展开


前置知识:康拓展开
逆康拓展开可以求解 1 1 1 ~ n n n的全排列中,字典序第 x x x个的排列。

逆康拓展开

比如,在 1 1 1 ~ 5 5 5的全排列当中,要求按字典序第 107 107 107的排列。
因为在康拓展开时,单调递增的序列算出的结果是 0 0 0,所以要将原数减一。
首先先让 x / x/ x/ ( n − 1 ) ! (n-1)! (n1)!,也就是 106 / ( 5 − 1 ) ! = 106 / 4 ! = 106 / 24 = 4 … … 10 106/(5-1)!=106/4!=106/24=4……10 106/(51)!=106/4!=106/24=4……10,那么就算出有 4 4 4个值比第一个值小,所以第一个值就是 5 5 5
接着把上次剩下的余数拿来继续除 10 / ( 4 − 1 ) ! = 10 / 3 ! = 10 / 6 = 1 … … 4 10/(4-1)!=10/3!=10/6=1……4 10/(41)!=10/3!=10/6=1……4,只有一个数比第二个值小,那么第二个值就是 2 2 2
下一次, 4 / ( 3 − 1 ) ! = 4 / 2 ! = 4 / 2 = 2 4/(3-1)!=4/2!=4/2=2 4/(31)!=4/2!=4/2=2,有两个数比第三个值小,那么第三个值就是 4 4 4,这时候 2 2 2已经被排过了,现在还没有排的元素是 1 1 1 3 3 3 4 4 4,故第三大的是 4 4 4
接着, 0 / ( 2 − 1 ) ! = 0 / 1 = 0 0/(2-1)!=0/1=0 0/(21)!=0/1=0,那就是当前没拍过的元素中第 1 1 1大,所以是 1 1 1
最后就只剩一个 3 3 3,那么最后排完就是 52413 52413 52413
思想就是康拓展开公式的逆用。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int read(){
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}
void print(int x){
	if(x<0)putchar('-'),x=-x;
	if(x<10){putchar(x+'0');return;}
	print(x/10);
	putchar(x%10+'0');
}
int n,k;
int a[N];
int f[N]={1};
signed main(){
	n=read(),k=read()-1;
	for(int i=1;i<=n;i++)a[i]=i,f[i]=f[i-1]*i;
	string s="";
	for(int i=1;i<=n;i++){
		int t=k/f[n-i];
		print(a[t+1]),putchar(' ');
		for(int i=t+1;i<=n;i++)a[i]=a[i+1];
		k%=f[n-i];
	}
}

或者也可以用动态数组。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int read(){
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}
void print(int x){
	if(x<0)putchar('-'),x=-x;
	if(x<10){putchar(x+'0');return;}
	print(x/10);
	putchar(x%10+'0');
}
int n,k;
vector<int>a;
int f[N]={1};
signed main(){
	n=read(),k=read()-1;
	for(int i=1;i<=n;i++)a.push_back(i),f[i]=f[i-1]*i;
	string s="";
	for(int i=1;i<=n;i++){
		int t=k/f[n-i];
		print(a[t]),putchar(' ');
		a.erase(a.begin()+t);
		k%=f[n-i];
	}
}

例题

Cow Line S
这是一道康拓展开加逆康拓展开的板子题。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+5;
int n,m,k;
vector<int>a;
int g[N];
int f[N]={1};
int bit[N];
void update(int x,int p){
	while(x<=n){
		bit[x]+=p;
		x+=x&-x;
	}
}
int query(int x){
	int res=0;
	while(x){
		res+=bit[x];
		x-=x&-x;
	}
	return res;
}
signed main(){
	ios::sync_with_stdio(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)f[i]=f[i-1]*i;
	while(m--){
		char op;
		cin>>op;
		if(op=='P'){
			cin>>k;
			k--;
			string s="";
			for(int i=1;i<=n;i++)a.push_back(i);
			for(int i=1;i<=n;i++){
				int t=k/f[n-i];
				cout<<a[t]<<' ';
				a.erase(a.begin()+t);
				k%=f[n-i];
			}
			cout<<'\n';
		}
		else{
			for(int i=1;i<=n;i++)cin>>g[n-i+1];
			int ans=0;
			for(int i=1;i<=n;i++){
				int res=query(g[i]-1);
				ans+=f[i-1]*res;
				update(g[i],1);
			}
			cout<<ans+1<<'\n';
			for(int i=1;i<=n;i++)update(g[i],-1);//记得清空树状数组
		}
	}
}
http://www.dtcms.com/a/111290.html

相关文章:

  • 强化中小学人工智能教育:塑造未来社会的科技基石
  • Linux C++编译及g++使用操作
  • Python集合(五)
  • PostgreSQL 删除数据库
  • Vanna:用检索增强生成(RAG)技术革新自然语言转SQL
  • 深入解析大型应用架构:以dify为例进行分析
  • goto在Java中的用法
  • 架构与大数据-RabbitMQ‌和Kafka的技术实现异同及落地场景上的异同
  • 数据库相关简介
  • MINIQMT学习课程Day9
  • leetcode 代码随想录 数组-区间和
  • Cortex-M 上编写汇编函数
  • 01-STM32(介绍、工具准备、新建工程)p1-4
  • 重要头文件下的函数
  • [Python学习日记-89] 并发编程之多进程 —— 共享数据、信号量、事件、进程池
  • 【nginx】Nginx的功能特性及常用功能
  • PostgreSQL:表分区与继承
  • OBS录制的一些日志
  • python如何把列表中所有字符变成小写
  • GATT(Generic Attribute Profile)是蓝牙低功耗(Bluetooth Low Energy,简称BLE)协议栈中的一个核心协议
  • 【蓝桥杯】算法笔记3
  • 函数栈帧的创建与销毁
  • 言语理解与表达
  • 实战交易策略 篇十四:江南神鹰捕捉热点和熊市生存交易策略
  • 专为 零基础初学者 设计的最简前端学习路线,聚焦核心内容,避免过度扩展,帮你快速入门并建立信心!
  • 第15周:注意力汇聚:Nadaraya-Watson 核回归
  • 生成 SSH Key 并配置 GitHub/GitLab 详细教程
  • 深入理解AOP:面向切面编程的核心概念与实战应用
  • ctfshow VIP题目限免 robots后台泄露
  • 规则引擎Drools