写这篇题解的目的主要是自我反思,最近两天一天打的比一天拉。虽然跟状态确实有一定关系,但我觉得这还反应了更多的问题,反应了我各方面的不足。
总结
虽然姑且比赛时做了四道,但是wa了好多,做的也慢,也维护了那一条负斜率。做题经验确实不足,思维水准也不够高。
题解
A. Phoenix and Gold
A题还是蛮简单的,题目大意是让我们将一个数组重新排序,使得按重新排序后的前缀和没有恰好等于x的,如果不能,则输出"NO",否则输出"YES"以及排序后的数组。
我们只需要贪心的思考即可,分析他给我们的数组,我们直接维护一个当前数组的前缀和,如果遇到一个和为x。我们就需要进行判断,如果当前位存在一个后继,直接交换当前位和后继,这样当前位的前缀和就不在是x,而从下一位开始,前缀和一定大于x (注意题目给定的数组的元素是互不相同的)。如果当前位不存在后继,则数组总和为x,那么我们不论怎么排序,最后的和一定是x。
代码(以下代码可以大大简化因为这里考虑了重复元素,请自行修改,不过我相信你们也不需要)
//#include<bits/stdc++.h> ----万能头----
#include <iostream>
#include<stdio.h>
#include<string>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<cmath>
#include<queue>
#include<list>
#include<map>
#include<unordered_map>
#include<stack>
#include<time.h>
using namespace std;
const int p = 1e9+7;
const int N = 2e4+5;
const int maxn = 1e5+5;
const long long INF = 1e18;
#define REP(i,n) for(ll i = 1; i <= n; ++i)
#define REPA(i,j,n) for(ll i = j; i <= n; ++i)
#define RREP(i,n) for(ll i = n; i >= 1; --i)
#define REPR(i,j,n) for(ll i = n; i >= j; --i)
#define lll __int128
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<double,ll> pdi;
int t;
ll n, m, k;
inline int read(){
char c=getchar();
int x=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
int x[N];
void solve() {
cin>>n>>m;
for(int i = 1; i<=n;++i)cin>>x[i];
ll sum = 0;
int pos = -1;
for(int i = 1 ; i <= n; ++i){
sum += x[i];
if(sum == m){
pos = i;
break;
}
}
if(pos == -1){
cout<<"YES"<<'\n';
for(int i = 1; i <= n; ++i)cout<<x[i]<<" ";
cout<<'\n';
return;
}
bool f = 1;
for(int i = pos + 1; i <= n;++i){//这里昨天考虑了可能重复元素的情况,实际上是没有的
if(x[i] != x[pos]){
swap(x[i],x[pos]);
f = 0;
break;
}
}
if(f){
cout<<"NO"<<'\n';
} else {
cout<<"YES"<<'\n';
for(int i = 1; i <= n; ++i)cout<<x[i]<<" ";
cout<<'\n';
}
}
int main(){
//ios::sync_with_stdio(0);
//cin.tie(0);
//cout.tie(0);
#ifdef ACM
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
t = 1;
//srand((unsigned)time(NULL));
scanf("%lld",&t);
while (t--)
solve();
fclose(stdin);
fclose(stdout);
}
B. Phoenix and Puzzle
B题呢一开始想的太简单了,虽然所有情况样例确实给出了,但对于边长的情况不知为啥会降智那么多QAQ,导致wa了好多发,后来发现了呢,又因为变量名写错,又wa了几发,导致这题基本没分了(我好菜啊)。
B题是给定我们一个数n,判定是否存在一个正方形由n个等腰直角三角形构成,首先不难想到n必须是偶数,由于我们的大正方形必须是由最小的两个基本正方形构成(即有两个等腰直角或四个等腰直角构成的两个最小正方形),所以这个数必须是2或者4的倍数,那么思考剩下的数会是什么(n/2或者n/4),由于我们要构成正方形,那么边长一定是原本最小正方形的若干倍(假设我们现在将最小正方形边长看成一),可以是两倍,也可以是三倍等等,这样对于每一个边长,我们需要的正方形个数就是边长的平方个。
代码如下
//#include<bits/stdc++.h> ----万能头----
#include <iostream>
#include<stdio.h>
#include<string>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<cmath>
#include<queue>
#include<list>
#include<map>
#include<unordered_map>
#include<stack>
#include<time.h>
using namespace std;
const int p = 1e9+7;
const int N = 2e4+5;
const int maxn = 1e5+5;
const long long INF = 1e18;
#define REP(i,n) for(ll i = 1; i <= n; ++i)
#define REPA(i,j,n) for(ll i = j; i <= n; ++i)
#define RREP(i,n) for(ll i = n; i >= 1; --i)
#define REPR(i,j,n) for(ll i = n; i >= j; --i)
#define lll __int128
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<double,ll> pdi;
int t;
ll n, m, k;
inline int read(){
char c=getchar();
int x=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
int x[N];
void solve() {
cin>>n;
if(n & 1){
cout<<"NO"<<'\n';
return;
}
/*if(n == 4){
cout<<"YES\n";
return;
}*/
ll tmp = n / 2;
ll now = sqrt((double)tmp);
if(now*now == tmp){
cout<<"YES"<<'\n';
return;
}
if(n % 4 == 0) {
tmp = n / 4;
now = sqrt((double) tmp);
if(now * now == tmp){
cout<<"YES"<<'\n';
return;
}
}
cout<<"NO"<<'\n';
}
int main(){
//ios::sync_with_stdio(0);
//cin.tie(0);
//cout.tie(0);
#ifdef ACM
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
t = 1;
//srand((unsigned)time(NULL));
scanf("%lld",&t);
while (t--)
solve();
fclose(stdin);
fclose(stdout);
}
C. Phoenix and Towers
C题更是一言难尽,一开始想复杂了,后来玩了会手机冷静下,就直接过了。。。
C题给了我们n个block(英语水平有限,不敢翻译),m个tower,每个block有一个高度h,这个高度不大于x,要求我们将n个block用尽,且每个tower都至少有一个block,要保证任意两个tower的高度差不超过x,对于这题,我们不能想的太复杂。我们仔细分析一下,注意到每个block高度不大于x,这个条件有什么用呢?我们分析一个已经合法的序列,去最小值mn,最大值mx,mx-mn<x(这里我就懒得用markdown进行美化了),如果我们把任意一个block给最小值,mn = mn + h[i] <mn + x,如果这个数依旧小于mx那没有任何问题,如果大于了mx,mn+h[i]-mx<h[i]<x,也就是说,如果我们每次给当前序列的最小值加上一个block,序列依旧是合法的,所以直接优先队列维护即可。
代码如下
//#include<bits/stdc++.h> ----万能头----
#include <iostream>
#include<stdio.h>
#include<string>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<cmath>
#include<queue>
#include<list>
#include<map>
#include<unordered_map>
#include<stack>
#include<time.h>
using namespace std;
const int p = 1e9+7;
const int N = 2e5+5;
const int maxn = 1e5+5;
const long long INF = 1e18;
#define REP(i,n) for(ll i = 1; i <= n; ++i)
#define REPA(i,j,n) for(ll i = j; i <= n; ++i)
#define RREP(i,n) for(ll i = n; i >= 1; --i)
#define REPR(i,j,n) for(ll i = n; i >= j; --i)
#define lll __int128
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<double,ll> pdi;
int t;
ll n, m, k;
struct node{
ll h;
ll id;
ll pos;
bool operator < (const node & w){
return id < w.id;
}
}e[N];
inline int read(){
char c=getchar();
int x=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
ll x;
void solve() {
cin>>n>>m>>x;
for(int i = 1; i <= n; ++i){
cin>>e[i].h;
e[i].id = i;
}
if(n < m){
cout<<"NO"<<'\n';
return;
}
priority_queue<PII,vector<PII>,greater<PII>> q;
for(int i = 1; i <= m; ++i){
q.push({0,i});
}
for(int i = 1; i <= n; ++i){
ll var = q.top().second;
ll sum = q.top().first;
q.pop();
sum += e[i].h;
e[i].pos = var;
q.push({sum,var});
}
// sort(e+1,e+1+n);
cout<<"YES"<<'\n';
for(int i = 1; i <= n; ++i){
cout<<e[i].pos<<" ";
}
cout<<'\n';
}
int main(){
//ios::sync_with_stdio(0);
//cin.tie(0);
//cout.tie(0);
#ifdef ACM
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
t = 1;
//srand((unsigned)time(NULL));
scanf("%lld",&t);
while (t--)
solve();
fclose(stdin);
fclose(stdout);
}
D. Phoenix and Socks
u1s1我觉得D题是前4题最简单的
D题直接贪心就完事了,如果当前有正好配对的socks直接移除,然后去分析剩下的,考虑到left sock的数量和right sock的数量可能不一致,(如果剩下的左右数正好相同,代价就是左(右)边的数量,由于证明很简单,请自行证明),根据对称性,我们只需要分析任意一边数量大的情况,那么我假设左边的sock (直接将sock分成左右两边) 数量大于右边的,我们思考每一个剩下的配对的花费,要么是2,要么就是1,我们贪心的想要获取1,减少2的存在,也就意味着我们需要在左边将适当数量的相同颜色的sock进行配对,这样便是最优的,证明很简单,自行证明,那么问题就解决了。
代码如下
//#include<bits/stdc++.h> ----万能头----
#include <iostream>
#include<stdio.h>
#include<string>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<cmath>
#include<queue>
#include<list>
#include<map>
#include<unordered_map>
#include<stack>
#include<time.h>
using namespace std;
const int p = 1e9+7;
const int N = 2e5+5;
const int maxn = 1e5+5;
const long long INF = 1e18;
#define REP(i,n) for(ll i = 1; i <= n; ++i)
#define REPA(i,j,n) for(ll i = j; i <= n; ++i)
#define RREP(i,n) for(ll i = n; i >= 1; --i)
#define REPR(i,j,n) for(ll i = n; i >= j; --i)
#define lll __int128
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<double,ll> pdi;
int t;
ll n, m, k;
struct node{
ll h;
ll id;
ll pos;
bool operator < (const node & w){
return id < w.id;
}
}e[N];
inline int read(){
char c=getchar();
int x=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
ll l , r;
int lc[N], rc[N];
int c[N];
void solve() {
cin>>n>>l>>r;
ll suml = l, sumr = r;
for(int i = 1; i <= n; ++i)cin>>c[i],lc[i]=0,rc[i] = 0;
for(int i = 1; i <= l; ++i)lc[c[i]]++;
for(int i = l + 1; i <= n; ++i){
rc[c[i]]++;
if(lc[c[i]]){
lc[c[i]]--;
suml--;
rc[c[i]]--;
sumr--;
}
}
ll cost = 0;
if(suml == sumr){
cout<<suml<<'\n';
return;
}
if(suml < sumr){
for(int i = l + 1; i <= n; ++i){
if(rc[c[i]] >= 2){
int get = rc[c[i]]/2;
if(sumr - 2*get < suml){
cost += (sumr-suml)/2; sumr = suml;
break;
}
sumr -= 2*get;
cost += get;
rc[c[i]] -= 2*get;
}
}
if(suml < sumr){
ll mid = suml + sumr >> 1;
cost += sumr - mid;
}
cost += ((suml + sumr)>>1);
cout<<cost<<'\n';
} else {
for(int i = 1; i <= l; ++i){
if(lc[c[i]] >= 2){
int get = lc[c[i]]/2;
if(suml - 2*get < sumr){
cost += (suml - sumr)/2;
suml = sumr;
break;
}
suml -= 2*get;
lc[c[i]] -= 2* get;
cost += get;
}
}
ll mid = (suml + sumr) >> 1;
if(suml > sumr){
cost += (suml - mid);
}
cost += mid;
cout<<cost<<'\n';
}
}
int main(){
//ios::sync_with_stdio(0);
//cin.tie(0);
//cout.tie(0);
#ifdef ACM
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
t = 1;
//srand((unsigned)time(NULL));
scanf("%lld",&t);
while (t--)
solve();
fclose(stdin);
fclose(stdout);
}
由于本人是个菜鸡,写的题解也是菜的一批,请多多谅解QAQ