ICPC-思维-CF#55div2 A+B+C+D

https://codeforces.com/contest/1082
退役了,就写写题解报告过日子了。
A
翻书:给你起点S,终点E,跳动间隔d,可以往前或者往后。但是到边界1或者N时,直接在边界上停止,再继续从边界跳到终点。问需要跳的步数。
1.判断S-E是否可以直接到达(用取余判断)
2.判断S-1-E方式可以成立吗?(用取余判断)
3.判断S-N-E方式可以成立吗?(用取余判断)
4.以上都不成立,-1

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const double eps=1e-9;
const int inf=1e9;
const int maxn=1e5;
ll n,x,y,d;
string s;
int cnt=0;
ll num[maxn+5];
int main(){
    ll ans=0;
    ios::sync_with_stdio(0);
    int q;
    cin>>q;
    while(q--){
        cin>>n>>x>>y>>d;
        ll ans=0;
        if(abs(y-x)%d==0){
            ans=abs(y-x)/d;
        }
        else{
            ans=inf;
            ll temp1,temp2;
            bool ju=false;
            if((n-y)%d==0){//hou
                temp1=(n-y)/d;
                temp1+=(n-x)/d+((n-x)%d>0);
                ans=min(ans,temp1);
                ju=true;
            }
            if((y-1)%d==0){//qian
                temp2=(y-1)/d;
                temp2+=(x-1)/d+((x-1)%d>0);
                ans=min(ans,temp2);
                ju=true;
            }
            if(!ju)ans=-1;
        }
        cout<<ans<<endl;
    }
    return 0;
}

B
给你一行由G和S组成的字符串,你可以任意调换一下两个位置,问最多的连续的G的长度。

  1. GG…G | S | GG…G 的情况
  2. GG…G |SSSSS 的情况
  3. GG…G | SS…S | GG…G的情况
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const double eps=1e-9;
const int inf=1e9;
const int maxn=1e5;
ll n,m,k;
string s;
int cnt=0;
ll num[maxn+5];
int main(){
    ll ans=0;
    ios::sync_with_stdio(0);
    int q;
    cin>>q;
    cin>>s;
    mem(num,0);
    cnt=0;
    int zheng=0;
    for(int i=0;i<q;){
        if(s[i]=='G'){
            int j=0;
            while(s[i+j]=='G'){
                num[cnt]++;
                j++;
            }
            i=i+j;
            cnt++;
            zheng++;
        }
        else if(s[i]=='S'){
            int j=0;
            while(s[i+j]=='S'){
                num[cnt]--;
                j++;
            }
            i=i+j;
            cnt++;
        }
    }
    if(zheng==1){
        sort(num,num+cnt);
        cout<<max(ans,num[cnt-1])<<endl;
    }
    else{
        for(int i=0;i<cnt;i++){
           if(num[i]>0&&num[i+1]==-1&&num[i+2]>0){
                if(zheng>2){
                    ans=max(ans,num[i]+num[i+2]+1);
                }
                else {
                    ans=max(ans,num[i]+num[i+2]);
                }
           }
           if(num[i]>0)ans=max(ans,num[i]+1);
        }
        cout<<ans<<endl;
    }
    return 0;
}

C
给你n个人,他有值为k(可负)的技能m。需要你寻找一个答案,使得不同技能的人数一样多(技能可以不用全选)但是你需要找一个最大的k的和。
肯定一开始都是想贪心暴力,能理解,但是这题有可能T,队友题解给的是队列优化,但是我自己写的是暴力剪枝。
注意:
1.每个技能排序完之后,前缀和求一下,得到不同人数下的值,这里就可以把前缀和小于0的全舍去。
2.对不同技能的前缀和,存一个人数的前缀和:如果这个技能在当下的人数的前缀和依然大于0,那么就可以加入当下的人数的前缀和里
3. 每一次更新前缀和,都找和的最大值。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const double eps=1e-9;
const int inf=1e9;
const int maxn=1e5;
ll n,m,d;
vector<ll>num[maxn+5];
int siz[maxn+5];
bool cmp(ll a,ll b){
    return a>b;
}
ll ans=0;
ll summ[maxn+5];
int main(){
    ios::sync_with_stdio(0);
    int  a;
    ll b;
    cin>>n>>m;
    for(int i=0;i<n;i++){
        cin>>a>>b;
        num[a].push_back(b);
    }
    int sizz=0;
    mem(summ,0);
    ans=0;
    for(int i=1;i<=m;i++){
        siz[i]=num[i].size();
        if(siz[i]<=0)continue;
        sort(num[i].begin(),num[i].end(),cmp);
        if(num[i][0]>0){
            summ[0]+=num[i][0];
            ans=max(ans,summ[0]);
        }
        for(int j=1;j<siz[i];j++){
            if(num[i][j-1]+num[i][j]>0){
                num[i][j]=num[i][j-1]+num[i][j];
                summ[j]+=num[i][j];
                ans=max(ans,summ[j]);
            }
            else {
                break;
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

下面是队友的队列写法

#include <algorithm>
#include <vector>
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
const int N=1e5+2;
priority_queue<int,vector<int> ,less<int> >q[100005];
ll sum[100005];//表示sum[i]表示有i个人参加单个项目的最大收益
int main()
{
    int tem1,tem2;
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>tem1>>tem2;
        q[tem1].push(tem2);
    }
    ll now=0,cnt=0,res=0;
    for(int i=1;i<=m;i++)
    {
        now=0;cnt=0;
        while(!q[i].empty())
        {
            now+=q[i].top();
            if(now>0)
            {
                sum[cnt++]+=now;
            }
            q[i].pop();
        }
    }
    for(int i=0;i<n;i++)
    {
        res=max(res,sum[i]);
    }
    cout<<res<<endl;
    return 0;
}

D
找一颗树,最大度数固定,问能否构成,若可以,找树有最长的直径的构造方式。

找主链,然后填入叶子,具体看看CF官方题解吧!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const double eps=1e-9;
const int inf=1e9;
const int maxn=500;
int a[maxn+10];
int main(){
    int n,m;
    cin>>n;
    ll sum=0;
    vector<int>ones;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum+=a[i];
        if(a[i]==1){
            ones.push_back(i);
            a[i]=0;
        }
    }
    if(sum<2LL*n-2)cout<<"NO\n";
    else{
        int onesize=ones.size();
        int len=(n-onesize)-1+min(2,onesize);//数的直径长度
        cout<<"YES "<<len<<"\n"<<n-1<<"\n";
        int edg=n-1;
        int lst=-1;
        if(!ones.empty()){//主链找一个头节点
            lst=ones.back();
            ones.pop_back();
        }
        for(int i=1;i<=n;i++){
            if(a[i]>1){
                if(lst!=-1){//有头节点
                    a[lst]--;
                    a[i]--;
                    cout<<lst<<" "<<i<<endl;
                }//形成主链
                lst=i;
            }
        }
        for(int i=n;i>=1;i--){//填上叶子节点
            while(!ones.empty()&&a[i]>0){
                a[i]--;
                cout<<i<<" "<<ones.back()<<endl;
                ones.pop_back();
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值