首页 > 其他分享 >根号分治入门

根号分治入门

时间:2022-12-24 11:56:05浏览次数:44  
标签:cnt 入门 int 复杂度 分治 leq 序列 排序 根号

根号分治

思想概述

根号分治,是应对序列问题的方法。对于一个序列问题,设置阀值\(S\),将数分为大于和小于两类,分类处理,达到优化复杂度的目的。\(S\)的大小具体分析。

[CCO2021] Swap Swap Sort

题目描述

你有一个长度为 \(n\) 的序列,每项都是不超过 \(k\) 的正整数。

你的朋友发明了一个排序算法,可以根据一个 \(1 \sim k\) 的排列对序列进行排序,排序后序列中任意两个不相等的数的相对位置与排列中的相对位置相同。他的算法只使用了邻项交换的操作,且总是保证操作次数最少。为了方便描述,他将这个 \(1 \sim k\) 的排列称为目标排列。

例如,序列为 \([1, 4, 2, 1, 2]\),目标排列为 \([4, 1, 2, 3]\),排序后为 \([4, 1, 1, 2, 2]\)。

你对你朋友的排序算法在目标排列不同时执行 swap 的次数很感兴趣。为了研究其中的规律,你一开始将目标排列设置为 \(1 \sim k\),并以此进行 \(q\) 次操作,每次操作交换目标排列中相邻的两个数的位置。每次交换后,你想知道如果用他的排序算法对原序列进行排序会执行 swap 的次数。

输入格式

第一行,三个整数 \(n, k, q\)。

第二行,\(n\) 个正整数 \(a_1, a_2, \cdots, a_n\),表示原序列。

接下来 \(q\) 行,每行一个正整数 \(b\),表示交换目标排列中的第 \(b\) 和第 \(b + 1\) 个数。

输出格式

对于每次询问,输出一个整数,表示所求的值。

样例 #1

样例输入 #1

5 4 3
1 4 2 1 2
3
2
1

样例输出 #1

4
2
2

对于 \(100\%\) 的数据,\(1 \leq n, k \leq 10^5\),\(1 \leq q \leq 10^6\),\(1 \leq a_i \leq k\),\(1 \leq b < k\)。

题解

首先,考虑目标排列是\(1\sim k\)的初始情况。容易证明:对于一次交换,能且仅能消除一个逆序对,所以最初需要的操作次数就是逆序对个数。

接着考虑将两个数\(x,y\)互换对答案造成的影响。

可以这样等效考虑,在目标情况中对两个数互换,等价于在排序后的序列中将\(x,y\)所在部分交换位置(因为\(x,y\)相邻)。进一步的,我们可以看作\(x'=y,y'=x\),目标序列不变,在原序列中把所有的\(x,y\)交换位置,再求逆序对是一样的。

所以因为\(x,y\)相邻,交换他们的位置只能影响这两个数的相互贡献。设在原序列中,有\(h(x,y)\)个数对\((i,j)\),满足\(i<j,a_i=x,a_j=y\),\(h'(x,y)\)个数对\((i,j)\),满足\(i>j,a_i=x,a_j=y\),则有\(h'(x,y)+h(x,y)=cnt_x\times cnt_y\)。

而交换两数位置,只会有:\(Ans=Ans+h'(x',y')-h(x',y')=Ans+cnt_{x'}\times cnt_{y'}-2h(x',y')\)问题转变为求\(h(x',y')\)。

因为\(h(x',y')\)乍一看不咋好求,考虑根号分治。

设阀值为\(S\),对于\(cnt_x\ge S\),可以\(O(n)\)地预处理出\(x\)与其他所有数之间的答案,这样的数不会超过\(\frac{n}{S}\),故空间复杂度\(O(\frac{n^2}{S})\),时间复杂度\(O(\frac{n^2}{S})\)。

对于一组询问\((x,y)\),\(cnt_x< S,cnt_y<S\),则可以开vector存储每个数出现的位置,双指针扫描一遍,复杂度\(O(qS)\)

总复杂度\(O(\frac{n^2}{S}+qS)\),根据均值不等式,\(\frac{n^2}{S}=qS\)时取得最小值,解得\(S=\frac{n}{\sqrt q}=100\),总复杂度\(O(n\sqrt q)\)。

不过在处理第一种情况的时候,由于空间复杂度为\(O(n\sqrt q)\),吃不消,可以离线下来,单独处理关于每个数的询问,空间复杂度降至\(O(n)\)。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
#define N 100050
#define Q 1000500
#define int long long
struct node{
	int x,y,id,tag;
};
vector<node>t[N]; 
vector<int>seat[N];
int n,m,k,q,s=100,c[N],ans,b[N],a[N],cnt1[N],cnt2[N],c1[N],Ans[Q],f[N],vis[N];
#define lowbit(x) x&-x
void add(int x,int k1){
	for(int i=x;i<=k;i+=lowbit(i))c1[i]+=k1;
}
int ask(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i))ans+=c1[i];
	return ans;
}
int get(int x,int y){//h(x,y) 
	int ans=0;
	int l=0,r=0,len1=seat[x].size(),len2=seat[y].size();
	for(r=0;r<len2;r++){
		while(seat[x][l]<seat[y][r]&&l<len1)l++;
		ans+=l;
	} 
	return (c[x]*c[y]-2*ans);
}
void init(){
	cin>>n>>k>>q;
	s=n/sqrt(q)+3;
	for(int i=1;i<=k;i++)b[i]=i;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)seat[a[i]].push_back(i);
	for(int i=1;i<=n;i++)c[a[i]]++;
	for(int i=n;i;--i){
		ans+=ask(a[i]-1);
		add(a[i],1);
	}
	for(int i=1;i<=q;i++){
		int xb;
		cin>>xb;
		swap(b[xb],b[xb+1]);
		int x=b[xb],y=b[xb+1];
		if(c[x]<s&&c[y]<s){
			if(c[x]==0||c[y]==0)Ans[i]=0;
			else Ans[i]=get(x,y);
		}
		else {
			if(c[x]<s)t[y].push_back((node){y,x,i,-1});
			else t[x].push_back((node){x,y,i,1});
		}
	}
	for(int i=1;i<=n;i++){//h(a[i],a[j]) 
		if(c[a[i]]<s||vis[a[i]])continue;
		memset(f,0,sizeof f);
		int cnt1=0;
		for(int j=1;j<=n;j++){
			if(a[j]==a[i])cnt1++;
			else{
				f[a[j]]+=cnt1;
			}
		}
		vis[a[i]]=1;
		int len1=t[a[i]].size();
		for(int j=0;j<len1;j++){
			Ans[t[a[i]][j].id]=t[a[i]][j].tag*(c[t[a[i]][j].x]*c[t[a[i]][j].y]-2*f[t[a[i]][j].y]);
		}
	}
	for(int i=1;i<=q;i++){
		ans+=Ans[i];
		cout<<ans<<endl;
	}
}
signed main(){
//	freopen("data.in","r",stdin);
//	freopen("data.ans","w",stdout);
	ios::sync_with_stdio(false);
	init();
}

标签:cnt,入门,int,复杂度,分治,leq,序列,排序,根号
From: https://www.cnblogs.com/oierpyt/p/17002694.html

相关文章

  • 算法--分治算法
    分治算法 一、算法思想  分治法作为一种常见的算法思想,其概念为:把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到最后子问题可以......
  • 省选06. 分治
    CF1442DSum设\(dp(i,j)\)表示前\(i\)个数组选\(j\)个元素的最大值。\[dp(i,j)=\max_{k=0}^jdp(i-1,k)+sum(i,k)\]因为数组内的元素单调不降,因此有一个结论,只有一......
  • 边分治 学习笔记
    边分治学习笔记就普遍理性而论,边分治能做的点分治也能做,可是难度…参考博客:边分治讲解前置:多叉树转二叉树也叫三度化。边分治在二叉树上表现得很优秀,是\(O(nlogn)\)......
  • angular开发从入门到入土第一节(angular环境搭建)
    第一步安装node(跳过)第二部安装angular-clinpminstall-g@angular/clicmd面板输入ngverisonangular脚手架安装成功运行cli命令ngnew工程名称 这......
  • 树状数组入门讲解
    树状数组长啥样constintN=1e3+10;intn;//数组范围intdata[N];//原始数据inttree[N];//树状数组本体inlineintlowbit(intx){returnx&(-x);}v......
  • FFT入门——学习笔记
    FFT入门给一个非常好的入门视频:快速傅里叶变换复数与单位根定义:\(i^2=-1\)为虚数单位,我们称形如\(a+bi(a,b\inR)\)的数为复数。我们可以用复数在复平面上表示点\((0,......
  • PPT 动画入门
    元素动画进入动画元素从无到有的过程退出动画元素从有到无的过程退出动画和进入动画,一对一强调动画在元素上变化的过程(如放大)动作路径3D动画三维动画低版本不支持组合动画......
  • 使用 IntelliJ IDEA 构建入门指南之一
    本指南将引导您使用IntelliJIDEA构建入门指南之一。您将构建的内容您将选择一个Spring指南并将其导入IntelliJIDEA。然后,您可以阅读指南,处理代码并运行项目。你需要什么......
  • Python从入门到精通(第2版)——pyuic5: error: no such option: -m的问题解决
    前言在学习《Python从入门到精通(第2版)》的第15章GUI界面编程——15.2.4将.ui文件转换为.py文件时,按照书中步骤出错时的问题解决,希望对同样学习本书的同学有所帮助。问......
  • AppScan入门工作原理详解-软件测试知识
    AppScan,即AppScanstandardedition。其安装在Windows操作系统上,可以对网站等Web应用进行自动化的应用安全扫描和测试。 RationalAppScan(简称AppScan)其......