搞心态了,第一题怎么交不了,害
A. Red Versus Blue
题意:给定n,r,b满足条件n=r+b且r>b,输出一个字符串又r个R和b个B,要求该字符串满足条件即连续的r或者b最少。
思路:这道题一开始在那找怎么连续最好,后面发现r>b,那么我们只要用B当作隔板将r分成b+1段即可,且每一段R的个数是r/段数+1或者r/段数(因为出发会向下取整,我们得拿出除法mods剩余的r来一个一个加入到原段中。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
int t;
cin>>t;
while(t--){
int n,r,b;
vector<int> v;
cin>>n>>r>>b;
int duan=b+1;
int less=r%duan;
for(int i=1;i<=less;i++){
for(int i=1;i<=r/duan+1;i++)cout<<"R";
cout<<"B";
}
for(int i=less+1;i<=duan;i++){
for(int j=1;j<=r/duan;j++)cout<<"R";
if(i!=duan)cout<<"B";
}
cout<<"\n";
}
}
B. Bit Flipping
题意:给定一个长度为n的01串,操作次数k,要求对这个01字符串进行k次操作后能得到的最大的值。操作内容为:每次选定字符串中的一个字符,其他字符都异或1,即1变成0,0变成1;
这道题卡了挺久,还被a题搞了心态交不上去,最后的c题又是赛后两分钟写出来(估计不会错)
思路:对于这道题,一开始不知道如何下手,一开始在那模拟半天,后面发现应该整体来看,单总操作次数为奇数时,如果该位时1的话,我们应该分配给他奇数次才能使得他最终还是1,如果该位位0的话,我们应该分配给他偶数次才能使得他最终变成1,又因为要得到最大数,那当然得从前往后枚举了,如果该位为1,我们分配给他1次,该位为0,我们分配给他0次,最终将剩余的次数全部留给最后一个,再对最后一位进行特判即可,操作次数位偶数的话九反过来即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
int t;
cin>>t;
while(t--){
int n,k;
cin>>n>>k;
string s;
vector<int> v,ans;
cin>>s;
int sum=k;
if(k%2==1){
for(int i=0;i<s.size()-1;i++){
//注意分配1次时要考虑他的总次数是否大于0,否则只能分配0;
if(s[i]=='1'&&sum>0)v.push_back(1),sum--;
else v.push_back(0);
}
v.push_back(sum);//将剩余次数都给最后一位
for(int i=0;i<s.size();i++){
//当前这一位为0,且操作次数为偶数或者当前这一位为1,操作次数为奇数,则答案是1,否则是0;
if((s[i]-'0')%2==v[i]%2)ans.push_back(1);
else ans.push_back(0);
}
for(int i=0;i<ans.size();i++)cout<<ans[i];
cout<<endl;
for(int i=0;i<v.size();i++)cout<<v[i]<<" ";
cout<<endl;
}
else {
for(int i=0;i<s.size()-1;i++){
if(s[i]=='0'&&sum>0)v.push_back(1),sum--;
else v.push_back(0);
}
v.push_back(sum);
for(int i=0;i<s.size();i++){
if((s[i]-'0')%2!=v[i]%2)ans.push_back(1);
else ans.push_back(0);
}
for(int i=0;i<ans.size();i++)cout<<ans[i];
cout<<endl;
for(int i=0;i<v.size();i++)cout<<v[i]<<" ";
cout<<endl;
}
}
}
C. Line Empire
题意:给定一个长度为n的数组pos,表示位置,开始位置为0(首都),从前往后走到终点且每一个位置都要到达。有两种操作。操作1:首都位置为pos,从pos1到pos2,你能消耗(pos2-pos)*b到达pos2 操作2:首都位置为pos,你能将首都移动到pos1,消耗(pos1-pos)*a;
这道题的话感觉会比b简单。赛后看旁边写的是dp,我也有想过dp,但是好像不满足无后效性遍没有继续尝试。接着,我又看了样例给的提示。发现好像能用前缀o(n)贪心解决掉问题.
我们发现我们最终的首都会在0到posi的某一个位置,于是我们枚举他,表示我们走到终点是首都是posi,那么我们会发现对于posi后面的所有位置posj,他们的消耗一定是(posj-posi)*b;
对于posi前面的位置,我们发现好像不能固定他的路径,他可能突然将首都从0直接搬到posi,又或者从前面的posk搬到posi,这样总消耗就不能确定了,但是我们发现,无论我们怎样将首都搬到posi,消耗一定是posi*a;然后我们就差计算前面这一段进行操作一的消耗了,我们会发现,当我们每次都选择搬首都的情况会使得消耗最少。
比如 0 1 5 6 21 30,0代表最初位置,假如我们最后将首都搬到5的话,对于5前面这一段的消耗,如果我们选择直接从0搬到5,则消耗为(5+5)*b,但是我们从0搬到1再搬到5,消耗为(1+4)*b,
最终消耗的一定会是每次都选择搬为最少消耗。最少消耗为posi*b;
这样,我们就把所有需要的东西都求到了呀,只需要用前缀和维护后面那一段就可以求总的消耗了,枚举完取最小值即为答案;
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2e2+10;
int main(){
ll t,pos[N]={0},sum[N]={0};
cin>>t;
while(t--){
ll n,a,b;
cin>>n>>a>>b;
for(int i=1;i<=n;i++)cin>>pos[i],sum[i]=sum[i-1]+pos[i];
ll ans=sum[n]*b;//一直都不搬首都的情况
for(int i=1;i<=n;i++){
ll tmp1=(sum[n]-sum[i]-pos[i]*(n-i))*b;//后面一段的消耗
ll tmp2=pos[i]*b;//搬家的消耗
ll tmp3=pos[i]*a;//前面一段的消耗
ans=min(ans,tmp1+tmp2+tmp3);
}
cout<<ans<<endl;
}
}
肝不动了,刚刚系统更新完了,交了过了,明天看看dp是个啥情况吧
d题一眼都没看到呜呜呜太菜了
补d题了
D. Reverse Sort Sum
题意:给定一个01串A,可以进行操作f(k,A):将A的前k个数字排序生成新的数组B;
如A{1 0 1 0} f(3,A)={0 1 1 0};最终将所有的b数组的每一位加起来的和得到c数组,输入c数组,要求你输出a数组;
总结:这道题早上看了也没啥思路。看完严格鸽的题解才懂的,还是太妙了,怎么感觉d题总是树状数组呜呜呜,还有线性的解法。
思路:从后往前枚举,第i位,当c数组个数大于等于位置数i时说明他这一位的a数组一定是1,如果小于i,我们可以知道他前面形成的排列已经足够将他的c数组变成i了,只有他为0,才可能在前面排列时不给他那么多的贡献。遍历完这个数后这个数就相当于从我们的世界消失了,于是将他的贡献消除即可。用差分数组来维护即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
const ll N=2e5+10;
struct BIT
{
const static int maxnum=2e5+10;
int tree[maxnum];
int lowbit(int x){
return x&(-x);
}
void add(int idx,int x){
for(int pos=idx;pos<maxnum;pos+=lowbit(pos)){
tree[pos]+=x;
}
}
int que(int n){
int ans=0;
for(int pos=n;pos;pos-=lowbit(pos)){
ans+=tree[pos];
}
return ans;
}
int que(int a,int b){
return que(b)-que(a-1);
}
void init(int n){
for(int i=0;i<=n+2;i++)tree[i]=0;
}
} bit;
signed main(){
int t,c[N],ans[N];
cin>>t;
c[0]=0;
while(t--){
int n,sum=0;
cin>>n;
bit.init(n);
for(int i=1;i<=n;i++){
cin>>c[i];
sum+=c[i];
bit.add(i,c[i]-c[i-1]);
ans[i]=0;
}
int cnt=sum/n;
for(int i=n;i>=1;i--){
int tot=bit.que(i);
if(tot>=i){
ans[i]=1;
bit.add(i-cnt+1,-1);
bit.add(i+1,1);
cnt--;
}
}
for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
cout<<endl;
}
}