Codeforces Round 873 (Div. 2)A-D1

文章探讨了几种编程问题,涉及数组元素必须为位置数倍数的条件、找到允许数组元素交换的最大间隔以及计算满足特定条件的数组重排方法。解决方案涵盖了数学和算法思维,如使用模运算、等差数列求和公式以及单调栈等方法。
摘要由CSDN通过智能技术生成

题单链接

A. Divisible Array

思路:题目要求每个位置的数都要为位置数的倍数,所以在此在此基础上只需要再考虑怎么样取没给位置上的位置数的倍数加起来等于n的倍数,起初我发现对于奇数个数,从1加到n一定可以被n整除,所以当n为奇数时直接输出1到n,而n为偶数时不满足此条件,但我们用1到n的和对n取模,这个数一定小于n,然后再将这个数加到对应位置的书数上,也就是对应位置的数乘2,然后输出即可

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1000;
int T,n,idx;
int res[N];
void solve()
{
	cin>>n;
	idx=0;
	if(n%2)
	{
		for(int i=1;i<=n;i++)
		{
			res[idx++]=i;
		}
	}
	else 
	{
		ll sum=0;
		for(int i=1;i<=n;i++)sum+=i;
		int x=sum%n;
		for(int i=1;i<=n;i++)
		{
			if(i==x)res[idx++]=2*i;
			else res[idx++]=i;
		}
	}
	for(int i=0;i<idx;i++)
	{
		if(!i)cout<<res[i];
		else cout<<" "<<res[i];
	}
	cout<<endl;
}
int main()
{
	cin>>T;
	while(T--)
	{
		solve();
	}
	return 0;
}

思路2:如果对于每个位置都取位置数的两倍,则会形成数列(2,4,6......2n),根据等差数列求和公式可得n项和为n(n+1),易知一定可以被n整除,所以只需要输出每个位置的位置数的两倍即可

代码:

#include <bits/stdc++.h>
 
using namespace std;
 
int main() {
    
    ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
 
    int t;
    cin >> t;
    while (t--) {
    	int n;
    	cin >> n;
    	for (int i = 1; i <= n; i++) cout << i * 2 << " ";
    	cout << "\n";
    }
}

B. Permutation Swap

题意:对于一个数组,数组中的任意两个相隔k的位置的数都可以交换位置,要求寻找一个最大数k,使得数组通过任意次交换可以使数列有序

思路:比赛时想麻烦了一直在考虑怎么对于每个位置上的数模上一个数可以使原数组成为一个循环数组(不过这思路好像也不对),而这题只需要将每个位置移动回原来该在的位置需要的交换距离求出来,然后对所有距离取一个gcd就可以了

代码:


#include <bits/stdc++.h>
#define endl '\n'
#define INF 0x3f3f3f3f
#define LL long long
//#define int long long
#define Mirai ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
using namespace std;
//typedef pair<int, int> PII;
const int N=2e5+10;
int pos[N];
int T,n,res;
void solve()
{
	res=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int num;
		cin>>num;
		pos[num]=i;
		res=__gcd(res,abs(pos[num]-num));
	}
	cout<<res<<endl;
 
}
int main()
{
	cin>>T;
	while(T--)
	{
		solve();
	}
	return 0;
}

C. Counting Orders

题意:给出两个数组a和b,要求重新对a进行排序,使得a中的每个值,都大于对应位置上的b数组中的值,输出有多少中重排方法

思路:对a和b进行从小到大排序,求出a中的每个值,其对应位置中的b的值及其前面的所有值中有多少个小于他的值,其代表的意义就是a这个数有多少种放置的方法,从小到大遍历a数组,每个数确定了一个放置位置,其后面的数因为大于这个数,所以后面的数的可放置位置也包括前面的数确定的放置位置,所以后面的数的放置位置数要对应-1,(其实就是个简单的排列组合),然后将每个位置的可放置位置数相乘就可以了

代码:

#include <bits/stdc++.h>
#define endl '\n'
#define INF 0x3f3f3f3f
#define LL long long
#define int long long
#define Mirai ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
using namespace std;
//typedef pair<int, int> PII;
const int N=2e5+10,mod=1e9+7;;
int a[N],b[N];
int res[N];
int n;
int ans;
void solve()
{
	ans=1;
	cin>>n;
	int pos=0;
	for(int i=0;i<n;i++)cin>>a[i];
	for(int i=0;i<n;i++)cin>>b[i];
	sort(a,a+n);
	sort(b,b+n);
	for(int i=0;i<n;i++)
	{
		while(a[i]>b[pos]&&pos<n)pos++;
		res[i]=pos;
	}
	//for(int i=0;i<n;i++)cout<<res[i]<<" ";
	int cnt=1;
	for(int i=1;i<n;i++)
	{
		res[i]-=i;
	}
	for(int i=0;i<n;i++)
	{
		ans=(ans%mod*res[i]%mod)%mod;
	}
	cout<<ans<<endl;
}
signed main()
{
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();
	}
}

D1. Range Sorting (Easy Version)

题意:对于一个数组区间 (l,r),将此区间进行排序,所需要的长度为l-r,题目给出一个数组,要求对该数组的所有子区间进行排序,所需要的长度和最小为多少

思路:

首先可以观察到,排序的区间是没有交叉的,因为如果交叉的区间我们一起排结果不会更差.

所以考虑把子段分成若干段,每段内的数排完序后仍然在这段之内,每有一段就可以让答案减一(不妨设初始答案为该段的长度).

这个东西是可以用一个栈来维护的.考虑增量的思想,我们每加入一个点,如果当前点大于之前的所有点,那么该点可以自成一段.

否则该点只能和之前的段合并,通过单调栈一直弹栈直到前一段的最大值小于当前加入的点即可.

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define pb push_back
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> PII;
const int N=5e3+10;
int n;
int arr[N];
stack<int> s;
void solve()
{
	cin>>n;
    for(int i=0;i<n;i++)cin>>arr[i];
    ll res=0;
    for(int i=0;i<n;i++)//枚举区间左端点
    {
        stack<PII> s;
        for(int j=i;j<n;j++)//枚举区间右端点
        {
            PII p={arr[j],arr[j]};//区间最小值和区间最大值
            while(s.size()&&s.top().second>arr[j])
            //如果栈顶区间的最大值大于当前元素,则该元素需要与栈顶区间融合
            //也就是排序的时候需要一起排
            {
                p.first=min(s.top().first,p.first);//更新区间最小值
                p.second=max(s.top().second,p.second);//更新区间最大值
                s.pop();//弹出栈顶区间
            }
            s.push(p);//当栈顶区间的最大值小于当前元素或者栈中没有区间的时候
            //说明已经不需要将当前元素继续向前融合区间
            res+=j-i+1-s.size();
        }
    }
    cout<<res<<endl;
}
int main()
{
    //Mirai;
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();
	}
	return 0;
}
/*
4 8 7 2
pair: 4 4
stack:{4,4}(区间最小值,区间最大值)
ans+=0
 
pair:8 8
stack:{4,4},{8,8}
ans+=0
 
pair:7 7-->7 8
stack:{4,4},{7,8}//4,8,7最后4为一个融合有序区间,7,8为一个融合有序区间
//要产生这些区间所需要的交换长度就为总区间长度减去融合有序区间数量
ans+=1
 
pair:2 2-->2 8
stack:{2,8}
ans+=3;
ans += j - i + 1 - stk.size();
stk里按顺序存储了各个不用与其他区间产生元素交换的有序区间
stk.size()就是通过对不同的区间进行排序之后产生的所有合法有序区间
 
*/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值