前言
这场其实D思路也很简单,就是一道树的直径的板子题,但是很麻烦没写出来,主要C本身也是比较麻烦的模拟题,所以这个D就没想做,这场模拟场,主要就是写起来会麻烦不少
题解部分
A. LRC and VIP
给定一个数组,要求让你把这个数组里的所有数分给两个集合,两个集合里面都要有数字,问你能否让两个集合里数字的最大公约数不同
你别说,刚开始我真没想到这里怎么贪心,是后面摸索着才想到的思路。首先我们要确认一点:对于几个数的最大公约数x,x肯定小于等于这几个数的最小值
有了这个结论我们就能很快做出来这道题了,首先排序,然后将最大值以外的数放一起,然后最大值单独放
注意特判最小值等于最大值的情况
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lowbit(x) x&(-x)
const int MOD=1e9+7;
const int N=1000100;
void init() {
}
int a[N];
void solve() {
init();
int n;
cin>>n;
int maxvalue=0;
int maxpos=0;
for(int i=1;i<=n;i++) {
cin>>a[i];
if(a[i] > maxvalue) {
maxvalue = a[i];
maxpos = i;
}
}
sort(a+1,a+n+1);
int gcdnum = a[1];
for(int i=2;i<n;i++) gcdnum = __gcd(gcdnum,a[i]);
if(a[n] == gcdnum) {
cout<<"NO\n";
return ;
}
cout<<"YES\n";
for(int i=1;i<=n;i++) {
if(i!=maxpos) cout<<1<<" ";
else cout<<2<<" ";
}
cout<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//cout<<prime[cnt-1]<<"\n";
//for(int i=1;i<=cnt;i++) cout<<prime[i]<<"\n";
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}
B. Apples in Boxes
有n个箱子,里面有
a
i
a_i
ai个苹果,选择拿掉一个箱子里的苹果,前提是里面有苹果,最后谁不能操作就输了。注意如果进行操作完每个箱子的最大苹果数量减去最小苹果数量大于给定的k的话,那么进行操作的那个人就输了。
在没有加粗条件的情况下,我们都知道看总数量的奇偶性就可以了。而这个加粗条件,仔细想想,因为是如果我操作完如果大于k的话,是我淘汰,所以我肯定尽量不想让这个差值大。很明显,只要操作前满足条件,那么我一定能做到操作之后也满足条件
所以这个条件只适用于最开始的时候不满足条件的时候,如果第一个人无论如何操作都无法满足条件,那么第一个人就输。反之我们就按照奇偶性来判断谁嬴谁输
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lowbit(x) x&(-x)
const int MOD=1e9+7;
const int N=1000100;
int a[N];
void solve() {
init();
int n,k;
cin>>n>>k;
map<int,int>mp;
int maxvalue=0;
int minvalue=MOD;
int sum=0;
for(int i=1;i<=n;i++) {
cin>>a[i];
mp[a[i]]++;
sum += a[i];
maxvalue=max(maxvalue,a[i]);
minvalue=min(minvalue,a[i]);
}
if(maxvalue - minvalue > k + 1 || (maxvalue - minvalue > k && mp[maxvalue] > 1 ) ) {
cout<<"Jerry\n";
return ;
}
if(sum % 2) cout<<"Tom\n";
else cout<<"Jerry\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//cout<<prime[cnt-1]<<"\n";
//for(int i=1;i<=cnt;i++) cout<<prime[i]<<"\n";
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}
C. Maximum Subarray Sum
首先给定n,k.给定一个长度为n的数组a,同时给定一个字符串,如果
s
i
s_i
si=1就表示记得
a
i
a_i
ai,如果
s
i
s_i
si=0就表示不记得
a
i
a_i
ai,此时
a
i
a_i
ai写作0,问你能否补齐不记得a数组的所有数字使得对于a数组里所有子序列来说,总和最大正好为k
一上来看这道题思路其实挺简单的,我要让总和最大正好为k,那么我们就要保证最开始这个数组的最大子序列和不超过k,注意由于存在待补充的位置,所以我们要把待补充位置当作分割线,将当前记得的位置分割成不同的几段,然后在每一段计算最大子序列和
现在我们保证最大子序列和不超过k了,那么我们又要怎么构造。想象一下,如果我有需要补充的位置,在满足加粗条件的情况下,我是肯定可以补充使得满足条件的,只要找到分隔的两端的最大和,然后补充上去,然后通过补充其他位置使得其他位置不存在最大子序列和超过k即可
思路还是挺好想的,重点是怎么写这个代码
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lowbit(x) x&(-x)
const int MOD=1e9+7;
const int N=1000100;
int a[N];
int b[N];
int pos[N];
void solve() {
init();
int n,k;
cin>>n>>k;
string s;
cin>>s;
int sum=0;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
int maxvalue=0;
pos[n+1]=n+1;
for(int i=n;i>=1;i--) {
//cout<<i<<"\n";
if(s[i-1] == '0') pos[i] = pos[i+1];
else pos[i] = i;
if(s[i-1]=='1') sum=max(a[i],sum+a[i]),maxvalue=max(maxvalue,sum);
else sum=0;
}
//cout<<"maxvalue="<<maxvalue<<"\n";
if(maxvalue > k) {
cout<<"NO\n";
return ;
}
int sign=0;
for(int i=1;i<=n;i++) {
//cout<<i<<"\n";
if(s[i-1]=='0') {
if(sign == 1) {
a[i]=-MOD;
continue;
}
sign = 1;
int left=i-1;
int right=pos[i];
int nowsum=0;
int maxvalue1=0;
int maxvalue2=0;
while(left >=1 && s[left-1]=='1') {
//cout<<left<<"\n";
nowsum+=a[left];
maxvalue1=max(maxvalue1,nowsum);
left--;
}
nowsum=0;
while(right<=n && s[right-1]=='1') {
nowsum+=a[right];
maxvalue2=max(maxvalue2,nowsum);
right++;
}
a[i]=k - maxvalue1 - maxvalue2 ;
i = pos[i] - 1;
}
}
if(sign || maxvalue == k) {
cout<<"YES\n";
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
cout<<"\n";
}
else cout<<"NO\n";
}
signed main() {
//ios::sync_with_stdio(false);
//cin.tie(0);
//cout.tie(0);
//cout<<prime[cnt-1]<<"\n";
//for(int i=1;i<=cnt;i++) cout<<prime[i]<<"\n";
int t=1;
cin>>t;
while(t--) {
solve();
}
return 0;
}