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;
}