逆序对 st表

D. Kill Anton 2200 逆序对性质

https://codeforces.com/problemset/problem/1526/D
题意:给定一个至多含有4种字符的字符串,定义一次操作为交换两个相邻的字符,给出一个s的排列使得其需要经过最多次操作才能还原为s。
n<=1e5
题解:我们先考虑最小操作次数如何实现。对于每个排列,还原其最少需要经过其逆序对次操作(相邻交换即每步至多减少1逆序对),而每次减少1逆序的操作总能实现,故最小操作次数即为逆序数。
故即为最大化逆序对数。我们可以先对其中某一个字符进行观察,不妨设其他字符均为y,考虑字符为x,则对于一个序列我们最大化其逆序对即为将其全部聚到最后。我们猜测最终每种颜色必然会在一起。
由于逆序对,对于某一个排列,若有xyyyyx,则y中与x成逆序的值是固定的,故对于其某一边的x,移到另一边一定不劣,故我们考虑将四种颜色分别聚在一起,之后枚举即可。

D,cut 2100

https://codeforces.com/problemset/problem/1516/D
题意:给一个a数组,给出q个询问,每次给定l->r,要将其切成几个片段使得每个片段LCM=MUL,问最少要切几个判断。
n,q,a[i]<=1e5
题解:lcm=mul即对于子序列中的每个素数只能称为其中一个的因数。故我们可以从后往前遍历,令f[i]为选取i->x的最大x,f[i]=min(f[i+1],minp{gcd(a[i],a[p]!=1}),即可得到以i为开头的最长子段。而对于选定开头后,贪心地每个取最长子段是最优的故我们可以用st表logn的查询最小数,详细见代码。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10,inf=1e9;
int a[N],nxt[N],v[N],pi[N],g[N][20],f[N],t;
void primes(){
	int n=2e5+1;
	for(int i=2;i<=n;i++){
		if(!v[i]) pi[++t]=i,v[i]=i;
		for(int j=1;j<=t;j++){
			if(v[i]<pi[j]||pi[j]>n/i) break;
			v[i*pi[j]]=pi[j];
		}
	}
}
signed main(){
	primes();
	int n,q;cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<N;i++) nxt[i]=inf;
	int p=inf;
	for(int i=n;i>=1;i--){
		int x=a[i];
		while(x!=1){
			int w=v[x];
			p=min(nxt[w],p);
			x/=w;
		}
		x=a[i];
		while(x!=1){
			int w=v[x];
			x/=w;
			nxt[w]=i;
		}
		f[i]=p;
	}
	
	for(int i=1;i<=n;i++){
		g[i][0]=f[i];
	}
	int ww=log2(n)+1;
	for(int i=1;i<=18;i++){
		for(int j=1;j<=n;j++){
			if(g[j][i-1]>n) g[j][i]=g[j][i-1];
			else g[j][i]=g[g[j][i-1]][i-1]; 
		}
	}
	while(q--){
		int l,r;cin>>l>>r;
		int w=0,x=l;
		int k=log2(r-l+1)+1;
		for(int i=18;i>=0;i--){
			if(g[x][i]-1<=r) w+=(1<<i),x=g[x][i];
		}
		if(x<=r)
		w+=1;
		cout<<w<<endl;
	}
}
posted @ 2023-06-05 22:17  wrong,anser  阅读(17)  评论(0)    收藏  举报