2023.5.7拷逝

T1

假设交换了 a[i]a[i+1] ,那么 a[1...i1]a[i+2...n] 与这两个数构成的逆序对不变。只有 a[i]a[i+1] 两个数构成的逆序对可能发生改变。

如果 a[i]<a[i+1] ,那么逆序对个数加+1;如果 a[i]=a[i+1] ,那么逆序对个数不变;如果 a[i]>a[i+1] ,那么逆序对个数1

另外,此题需要特别注意:当 n=1e6 时,逆序对个数最多可能为 n×(n1)/2 ,需要用long long

code:

#include<iostream>
#include<cstdio>
using namespace std;
long long n,m,c,ans,x,a[1000005];
int main(){
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	scanf("%lld%lld%lld",&n,&m,&c);
	for(int i=1;i<=n;++i)
		scanf("%lld",&a[i]);
	for(int i=1;i<=m;++i){
		scanf("%lld",&x);
		if(a[x]<a[x+1])
			++c;
		else if(a[x]>a[x+1])
			--c;
		ans^=c;
		swap(a[x],a[x+1]);
	}
	printf("%lld\n",ans);
	fclose(stdin);fclose(stdout);
	return 0;
}

T2

不难发现,如果存在逆序对,那么最近的逆序对距离一定是1。所以只需要检查这个数列是否存在逆序对,即是否不单调递增即可。

code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int t,n,a[5];
int read(){
	char g;int w=0,f=1;bool ok=0;
	while(1){
		g=getchar();
		if(g=='-')
			ok=1,f=-1;
		else if(g>='0'&&g<='9')
			ok=1,w=w*10+f*(int)(g-'0');
		else if(ok) break;
	}
	return w;
}
int main(){
	freopen("distance.in","r",stdin);
	freopen("distance.out","w",stdout);
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		int ok=0;
		for(int i=1;i<=n;++i){
			a[i&1]=read();
			if(i!=1&&a[i&1]<a[(i-1)&1])
				ok=1;
		}
		printf("%d\n",ok);
	}
	fclose(stdin);fclose(stdout);
	return 0;
}

T3

对于一个数 a[i] ,设 a[j](j>i) 是整个数列中最靠后的小于 a[i] 的数。此时 a[i]a[j] 能够连边。对于任意的 k(i<k<j) :① a[k]<a[j]<a[i] ,此时 ik 能连边;② a[j]<a[k] ,此时 jk 能连边。综上,我们发现: [i,j] 中的所有数一定处在一个联通块中。

首先预处理一个数组 minn[i] ,表示 [i,n] 中所有数的最小值。

接下来扫描这个序列。设当前的数下标为 it 为当前数所在联通块的右端点。

i>t 时,说明当前的数已经离开了上一个联通块,出现了一个新联通块。

之后从 t 开始扫描后面的数,设扫描的数为 j ,直到 minn[j]<=a[i]&&minn[j+1]>a[i] 时停止循环,并用 j 更新 t 。因为此时 a[j] 是整个数列中最靠后的小于 a[i] 的数,[i,j] 中的所有数一定处在一个联通块中,导致 i 所处的联通块得以扩充。

code:

#include<iostream>
#include<ctime>
#include<cstdio>
using namespace std;
int n,a[5000005],ans,t,minn[5000005],st=clock();
int read() {
    int s=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9')	{if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){s=(s<<3)+(s<<1)+(ch^48);ch=getchar();}
    return s*f;
}
int main(){
	//freopen("block.in","r",stdin);
	//freopen("block.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		a[i]=read();
	minn[n]=a[n];
	if(minn[n]<=a[1])
		printf("1\n");
	else{
		for(int i=n-1;i>=1;--i){
			minn[i]=min(minn[i+1],a[i]);
			if(!t&&minn[i]<=a[1]&&minn[i+1]>a[1])
				t=i;
		}
		ans=1;
		for(int i=2;i<=n;++i){
			if(i>t){
				++ans;t=i;
			}
			for(int j=t;j<=n;++j){
				if(j==t&&minn[j]>a[i])
					break;
				if((minn[j]<=a[i]&&minn[j+1]>a[i])||(j==n&&minn[j]<=a[i])){
					t=j;break;
				}
			}
		}
		printf("%d\n",ans);
	}
	fclose(stdin);fclose(stdout);
	return 0;
}

T4(50pts)

首先不难想到朴素的DP:设 f[i][j] 表示前 i 个数,逆序对个数为 j 的方案数。对于第 i 个数,它与前面的数可以构成 012 ...或 i1 个逆序对。因此 f[i][j] 可以由 f[i1][k](max(ji+1,0)<=k<=j) 转移而来。状态转移方程:f[i][j]=f[i1][k](max(ji+1,0)<=k<=j)。时间复杂度O(n3)

接下来考虑优化。设当for 循环到 j 时, s=f[i1][k](max(ji+1,0)<=k<=j)。那么当for 循环到 j1 时, s=f[i1][k](max(ji,0)<=k<=j1) ,只需令此时的 s 加上 f[i1][j]f[i1][ji] ,即可得到 j 时的 s ,然后直接用 s 更新 f[i][j] 。时间复杂度O(n2)

code:

#include<iostream>
#include<cstdio>
using namespace std;
long long n,m,f[2][1000005],s;
const long long mod=1e9+7;
int main(){
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	f[0][0]=1;
	for(int i=1;i<=n;++i){
		s=0;
		for(int j=0;j<=m;++j){
			if(j-i>=0)
				s=(s-f[(i-1)&1][j-i]+mod)%mod;
			s=(s+f[(i-1)&1][j])%mod;
			f[i&1][j]=s%mod;
		}
  	}
	printf("%lld\n",f[n&1][m]%mod);
	fclose(stdin);fclose(stdout);
	return 0;
}
posted @   andy_lz  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示