Codeforces Round #661 (Div. 3) Coda的题解集

A. Remove Smallest

读题读了很长时间,本质其实就是问排序后序列中有没有断档,可以桶排也可以Sort然后一个个判断。

#include
using namespace std;
int cas,n,a[55];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>cas;
    while(cas--)
    {
        memset(a,0,sizeof(a));
        cin>>n;
        for(int i=1; i<=n; ++i)
            cin>>a[i];
        sort(a+1,a+n+1);
        int flag=1;
        for(int i=2; i<=n; ++i)
            if(abs(a[i]-a[i-1])>1)
            {
                cout<<"NO"<<endl;
                flag=0;
                break;
            }
        if(flag)
            cout<<"YES"<<endl;
    }
}

B. Gifts Fixing

对于candy和orange分别在输入的时候找出最小值作为标准,再跑一遍顺序,做min(ai,bi)次同减,再对剩下的一方操作max(ai,bi)-min(ai,bi)次,两者相加,相当于每一对的最大操作次数就是max(ai,bi)

#include
#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
int cas,n,a[55],b[55],mina,minb,ans;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>cas;
    while(cas--)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        ans=0;
        mina=minb=inf;
        cin>>n;
        for(int i=1; i<=n; ++i)
        {
            cin>>a[i];
            mina=min(mina,a[i]);
        }
        for(int i=1; i<=n; ++i)
        {
            cin>>b[i];
            minb=min(minb,b[i]);
        }
        for(int i=1; i<=n; ++i)
        {
            a[i]-=mina;
            b[i]-=minb;
            ans+=max(a[i],b[i]);
        }
        cout<<ans<<endl;
    }
}

C. Boats Competition

数据很小,桶装数据,枚举s,再枚举其中一个人的重量i,然后在桶里找有没有重量为s-i的人,如果有就是一对。两人重量相同要特判。

#include
#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
int cas,n,x,w[200],maxw,ans;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>cas;
    while(cas--)
    {
        memset(w,0,sizeof(w));
        ans=maxw=0;
        cin>>n;
        for(int i=1; i<=n; ++i)
        {
            cin>>x;
            ++w[x];
            maxw=max(maxw,x);
        }
        for(int s=2;s<=maxw*2;++s)
        {
            int tmp=0;
            for(int i=1;i<=s/2;++i)
                if(i==(s-i))
                    tmp+=w[i]/2;
                else
                    tmp+=min(w[i],w[s-i]);
            ans=max(ans,tmp);
        }
        cout<<ans<<endl;
    }
}

D. Binary String To Subsequences

开两个vector,把head为0的和head为1的垃圾分类,遇到0就找head为1的接在上面再整体扔到另一边去,遇到1就找head为0的接头。如果不需要输出第二行,只需要两个变量记录两种head的数量就能做出来。

#include
#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=2e5+7;
int cas,n,head0,head1,ans,cnt,mem[maxn];
string s;
queue<int> q0,q1;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>cas;
    while(cas--)
    {
        cin>>n>>s;
        while(!q0.empty()) q0.pop();
        while(!q1.empty()) q1.pop();
        cnt=head0=head1=0;
        for(int i=0;i<n;++i)
        {
            int num;
            if(s[i]=='0')
            {
                if(head1)
                {
                    num=q1.front();
                    q0.push(num);
                    q1.pop();
                    --head1;
                }
                else
                {
                    num=++cnt;
                    q0.push(cnt);
                }
                ++head0;
                mem[i]=num;
            }
            else
            {
                if(head0)
                {
                    num=q0.front();
                    q1.push(num);
                    q0.pop();
                    --head0;
                }
                else
                {
                    num=++cnt;
                    q1.push(cnt);
                }
                ++head1;
                mem[i]=num;
            }
        }
        cout<<head0+head1<<endl;
        for(int i=0;i<n;++i)
            cout<<mem[i]<<" ";
        cout<<endl;
    }
}

E1. Weights Division (easy version)

贪心,题目给出n个点n-1条边的树,求出要使得每个叶子节点到根的路径权值和之和sum不大于S的最小操作次数,每次操作可以把某条边的权值/2。当对一条权值为w的边操作后,每一条包含这条边的路径都会受到影响,计该边被使用的次数为t,边对sum的贡献即为t*w,权值从w变成floor(w/2),减少了w-floor(w/2),sum就减少了t*(w-floor(w/2))。因为是通过最少的操作次数使得sum减少到规定的值S,可以想到以操作该边时sum的减少量为排序依据,贪心地选取使sum减少量最大的操作,直至sum<=S。因为每次操作后还要把操作后的边放回去参与下一轮贪心,这里为了方便可以选择优先队列,自定义排序函数。

#pragma GCC optimize(3)
#include
#define int long long
using namespace std;
typedef pair<int,int> P;
struct edge
{
    int to,we;
};
struct cmp {
    bool operator()(P a, P b)
    {
        return (a.first*a.second-(a.first/2)*a.second)<(b.first*b.second-(b.first/2)*b.second);
    }
};
const int maxn=1e5+7;
int cas,n,s,u,v,w,sum,ans;
vector<edge> g[maxn];
priority_queue<P,vector<P>,cmp> pq;
int dfs(int x,int pre)
{
    if(g[x].size()==1&&x!=1)
        return 1;
    int res=0;
    for(auto e:g[x])
        if(e.to!=pre)
        {
            int tmp=dfs(e.to,x);
            res+=tmp;
            sum+=tmp*e.we;
            pq.push(P(e.we,tmp));
        }
    return res;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>cas;
    for(int k=1;k<=cas;++k)
    {
        while(!pq.empty()) pq.pop();
        sum=ans=0;
        cin>>n>>s;
        for(int i=1;i<=n;++i) g[i].clear();
        for(int i=1; i<n; ++i)
        {
            cin>>u>>v>>w;
            g[u].push_back((edge){v,w});
            g[v].push_back((edge){u,w});
        }
        dfs(1,0);
        while(sum>s)
        {
            P p=pq.top();pq.pop();
            sum-=(p.first*p.second-(p.first/2)*p.second);
            ++ans;
            pq.push(P(p.first/2,p.second));
        }
        cout<<ans<<endl;
    }
}

E2. Weights Division (hard version)

贪心+二分,和上一题题面基本一致,给每个边加了一个1或2的价格,求的值变成了最小费用。可能一开始能想到通过性价比贪心,但是其实是错误的,因为只要把sum减到不大于S就行,如果目前只需要再减1就满足目标,面前有一个费用2减100和费用1减1的选项,贪心会选择费用2,从而多浪费了1点费用。在正确的解法中,可以把cost为1和cost为2的边分别贪心,在选择时对于cost相同的选项,当然优先选择减少sum多的。贪心方法依然和E1一样,把贪心结果按照顺序存储起来,并且做前缀和。此时枚举选择cost为1的结果的前i个和cost为2的结果的前j个,如果sum-res[1][i]-res[2][j]<=s即为可行答案,与ans比较取最小。这里还有明显的二分优化,当枚举i之后,可以从上面的式子得到res[2][j]>=sum-res[1][i]-s其中右式全部已知,可以以此在res[2]里二分搜索合法的j使得式子成立。

#pragma GCC optimize(3)
#include
#define int long long
using namespace std;
typedef pair<int,int> P;
struct cmp {
    bool operator()(P a, P b)
    {
        return (a.first*a.second-(a.first/2)*a.second)<(b.first*b.second-(b.first/2)*b.second);
    }
};
struct edge
{
    int to,we,co;
};
const int maxn=1e5+7;
const int inf=0x3f3f3f3f;
int cas,n,s,u,v,w,c,sum,ans;
vector<edge> g[maxn];
vector<int> res[3];
priority_queue<P,vector<P>,cmp> q[3];
int dfs(int x,int pre)
{
    if(g[x].size()==1&&x!=1)
        return 1;
    int res=0;
    for(auto e:g[x])
        if(e.to!=pre)
        {
            int tmp=dfs(e.to,x);
            res+=tmp;
            sum+=e.we*tmp;
            q[e.co].push(P(e.we,tmp));
        }
    return res;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>cas;
    while(cas--)
    {
        cin>>n>>s;
        sum=0;
        ans=inf;
        for(int i=1;i<=n;++i) g[i].clear();
        for(int i=1;i<n;++i)
        {
            cin>>u>>v>>w>>c;
            g[u].push_back((edge){v,w,c});
            g[v].push_back((edge){u,w,c});
        }
        while(!q[1].empty()) q[1].pop();
        while(!q[2].empty()) q[2].pop();
        dfs(1,0);
        for(int k=1;k<=2;++k)
        {
            int tmp=0;
            res[k].clear();
            res[k].push_back(0);
            while(!q[k].empty()&&(sum-tmp>s))
            {
                P p=q[k].top();q[k].pop();
                int val=p.first*p.second-(p.first/2)*p.second;
                if(val==0) break;
                tmp+=val;
                res[k].push_back(val+res[k][res[k].size()-1]);
                q[k].push(P(p.first/2,p.second));
            }
        }
        for(int i=0;i<res[1].size();++i)
        {
            vector<int>::iterator lb=lower_bound(res[2].begin(),res[2].end(),sum-s-res[1][i]);
            if(lb==res[2].end())
                continue;
            else
                ans=min(ans,i+2*(lb-res[2].begin()));
        }
        cout<<ans<<endl;
    }
}

F. Yet Another Segments Subset

离散化+区间DP,因为之前没有做过区间DP的题,代码参考了网页链接。使用的递归的方法DP,f[i][j]表示从i到j这一段内合法的最大线段数,递推式:f[i][j]=(存在线段正好l=i且r=j)+max(f[i+1][j],f[i][k]+f[k+1][j]),其中i<=k<=j

PS:多组数据时尽量用循环,按需初始化,这道题如果每次都把f数组memset,会T。

#include "bits/stdc++.h"
using namespace std;
const int maxn=6010;
int cas,n,cnt,l[maxn],r[maxn],p[maxn],f[maxn][maxn];
vector<int> v[maxn];
int dp(int l,int r)
{
    if(f[l][r]!=-1) return f[l][r];
    if(l>r) return 0;
    f[l][r]=dp(l+1,r);
    for(auto subr:v[l])
    {
        if(subr>=r) continue;
        f[l][r]=max(f[l][r],dp(l,subr)+dp(subr+1,r));
    }
    return f[l][r]=f[l][r]+count(v[l].begin(),v[l].end(),r);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>cas;
    while(cas--)
    {
        cin>>n;
        cnt=0;
        for(int i=1;i<=n;++i)
        {
            cin>>l[i]>>r[i];
            p[++cnt]=l[i];
            p[++cnt]=r[i];
        }
        sort(p+1,p+cnt+1);
        cnt=unique(p+1,p+cnt+1)-(p+1);
        for(int i=1;i<=cnt;++i)
        {
            v[i].clear();
            for(int j=1;j<=cnt;++j)
                f[i][j]=-1;
        }
        for(int i=1;i<=n;++i)
        {
            l[i]=lower_bound(p+1,p+cnt+1,l[i])-p;
            r[i]=lower_bound(p+1,p+cnt+1,r[i])-p;
            v[l[i]].push_back(r[i]);
        }
        cout<<dp(1,cnt)<<endl;
    }
}

这样就把坑填完了!

你可能感兴趣的:(Codeforces Round #661 (Div. 3) Coda的题解集)