Codeforces Round #696 (Div. 2)

CodeForces - 1474C   Array Destruction (思维,枚举)

题目链接:https://vjudge.net/problem/CodeForces-1474C

题目大意:

给定一个长度为 2n 的序列 a1,a2,...,a2n ,最开始,选定一个数 x ,接下来执行若干步操作:

       1.在序列中选出 ai ,aj 满足 ai + aj = x 

       2.将两者从数组中删除,并更新 x = max( ai , aj )

问是否能删除整个序列,如果可以,输出 YES 并输出 x 和一组可行的方案,如果不能,则输出 NO

解题思路:

因为,每组中最大的作为后续删除的 x ,所以可以知道最大的数一定最先删除

之后,枚举第二个数,进行判断。很显然,每次删除的一组数中,必然包含当前数的最大值,用 x 减去当前的最大值,得到另一个数;

若这个数存在,则继续进行删除,直到所有数都删除即可,一种可行解就行,否则,则退出,说明当前的方案不可行。

具体详见代码

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <utility>
#define cla(a, sum) memset(a, sum, sizeof(a))
#define rap(i, m, n) for(int i=m; i<=n; i++)
#define rep(i, m, n) for(int i=m; i>=n; i--)
#define bug printf("???\n")
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
typedef pair<ll, ll> Pl;
const int Inf = 0x3f3f3f3f;
const double eps = 1e-8;
const int mod=998244353;
const int maxn = 2e3+5;
const int N=1e6+5;
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

int t,n,Nn;
int a[maxn],ans1[maxn],ans2[maxn];
map<int,int>mp,b;

int solve(int c){//判断是否可行 
	int pre=a[n],pos=0;
	mp[pre]--;mp[c]--;//删除 
	ans1[++pos]=pre;ans2[pos]=c;
	for(int i=n-1;i&&pos<n;i--){
		c=a[i];//当前的最大值 
		if(mp[c]==0)continue;
		mp[c]--;//删除 
		pre=ans1[pos];
		if(mp[pre-c]==0)return 0;
		mp[pre-c]--;//删除 
		ans1[++pos]=c;ans2[pos]=pre-c;
	} 
	return 1;
}

int main()
{
	//ios::sync_with_stdio(false),cin.tie(0);
	cin>>t;
	while(t--){
		read(Nn);
		n=2*Nn;
		b.clear();//初始化 
		rap(i,1,n){
			read(a[i]);
			b[a[i]]++;//标记 
		}
		sort(a+1,a+n+1);
		int ok=0;
		rep(i,n-1,1){//枚举第二个数
			mp=b;
			ok=solve(a[i]);
			if(ok)break;
		}
		if(ok==0){
			printf("NO\n");
			continue;
		}
		printf("YES\n");
		printf("%d\n",ans1[1]+ans2[1]);
		for(int i=1;i<=Nn;i++){
			printf("%d %d\n",ans1[i],ans2[i]);
		}
	}

	return 0;
}

 

CodeForces - 1474D  Cleaning  (思维,前缀和)

题目链接: https://vjudge.net/problem/CodeForces-1474D

题目大意:

 给你一个数组,每次可以选择两个相邻的数,同时减小1;而且允许交换两个数,最多操作一次。问是否能最后全变成0。

解题思路:前缀和,后缀和

第一个数,肯定是和第二个数一起减;同理,第二个数还要和第三个数一起减。那么从左往右这样可以一直减下去。就变成了一个,a1,a2-a1,a3-a2+a1...这样的数组。维护这样的数组作为前缀数组 pre ;同理,维护后缀数组 suf 。

若是处理完后,pre[n]=0 ,则说明,不用交换即可全变成0。

否则,需要进行相邻交换。

对于第 i 个数,如果要交换的是 a[i] 和 a[i+1] ,那么受到影响的是 pre[i] 和 suf[i+1] 。

pre[i]=a[i+1]-pre[i-1] ; suf[i+1]=a[i]-suf[i+2]。如果现在两值相等,那么说明交换之后,相互抵消,满足全变成0。

注意:数组中全为非负数,若 a[i]<pre[i-1] 显然第 i 个数需要和相邻交换,赋值 pre[i] = -1 ,进行标记。同理,suf[i] 也一样。

具体详见代码 

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <utility>
#define cla(a, sum) memset(a, sum, sizeof(a))
#define rap(i, m, n) for(int i=m; i<=n; i++)
#define rep(i, m, n) for(int i=m; i>=n; i--)
#define bug printf("???\n")
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
typedef pair<ll, ll> Pl;
const int Inf = 0x3f3f3f3f;
const double eps = 1e-8;
const int mod=998244353;
const int maxn = 2e5+5;
const int N=1e5;
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

int t,n;
ll a[maxn],pre[maxn],suf[maxn];

int main()
{
	//ios::sync_with_stdio(false),cin.tie(0);
    read(t);
    while(t--){
    	read(n);
    	pre[0]=0;//初始化 
    	rap(i,1,n){
    		read(a[i]);
    		if(pre[i-1]==-1||a[i]<pre[i-1]){
    			pre[i]=-1;//标记 
			}
    		else pre[i]=a[i]-pre[i-1];
		}
		suf[n+1]=0;//初始化 
		rep(i,n,1){
			if(suf[i+1]==-1||a[i]<suf[i+1]){
				suf[i]=-1;//标记 
			}
			else suf[i]=a[i]-suf[i+1];
		}
		int ok=0;
		if(pre[n]==0)ok=1;//不需交换 
		if(ok){
			printf("YES\n");
			continue;
		}
		for(int i=1;i<n;i++){
			if(pre[i-1]==-1||suf[i+2]==-1){//说明i-1或i+2需要与其相邻的交换,i和i+1不能在进行交换 
				continue;
			}
			//注意要非负 
			if(a[i+1]>=pre[i-1]&&a[i]>=suf[i+2]&&a[i+1]-pre[i-1]==a[i]-suf[i+2]){
				ok=1;
				break;
			}
		}
		if(!ok)printf("NO\n");
		else printf("YES\n");
	}

	return 0;
}

CodeForces - 1474E   What Is It?  (思维,构造) 

题目链接:https://vjudge.net/problem/CodeForces-1474E

题目大意:

一个排列,你可以选择 i,j,i≠j,满足 a[j]=i,然后交换 a[i],a[j],交换收益为 (i-j)^2。

让你构造一个长度为n的排列,使得交换过程中所得收益最大。

思路:

每交换一次,都有一个数归位(即a[i]=i),所以我们最多交换n-1次。

我们逆向思考,起初排列中a[i]=i。

第一次交换1和n,接下来继续交换,每次交换的位满足a[i]=i,为了使得收益最大,第二次交换2和n,第三次交换1和n-1。可以发现,距离为n-1的只有一对即(1,n),距离为n-2的有两对,(2,n)和(1,n-1),其他的也是一样,最多两对。

所以,收益为:(n-1)^2+2*(n-2)^2+2*(n-3)^2...

注意输出格式

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <utility>
#define cla(a, sum) memset(a, sum, sizeof(a))
#define rap(i, m, n) for(int i=m; i<=n; i++)
#define rep(i, m, n) for(int i=m; i>=n; i--)
#define bug printf("???\n")
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
typedef pair<ll, ll> Pl;
const int Inf = 0x3f3f3f3f;
const double eps = 1e-8;
const int mod=998244353;
const int maxn = 1e5+5;
const int N=1e5;
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

int t,n;
int a[maxn];
vector<P>vec;

int main()
{
	//ios::sync_with_stdio(false),cin.tie(0);
	read(t);
	while(t--){
		vec.clear();
		read(n);
		rap(i,1,n)a[i]=i;//初始化 
		int ans=n-1;//最多交换n-1次 
		ll sum=0;//记录收益 
		rap(i,1,n-1){
			swap(a[i],a[n]);
			vec.push_back({i,n});
			ans--;sum=sum+(ll)(n-i)*(ll)(n-i);
			if(ans==0)break;
			if(i==1)continue;//第一次交换,只交换一次 
			swap(a[1],a[n-i+1]);
			vec.push_back({n-i+1,1});
			ans--;sum=sum+(ll)(n-i)*(ll)(n-i);
			if(ans==0)break;
		}
		printf("%lld\n",sum);
		rap(i,1,n){
			printf("%d",a[i]);
			if(i==n)printf("\n");
			else printf(" ");
		}
		printf("%d\n",n-1);
		int c=vec.size();
		for(int i=c-1;i>=0;i--){//逆序输出 
			printf("%d %d\n",vec[i].first,vec[i].second);
		}
	}

	return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值