Codeforces Round #661 (Div. 3)

A - Remove Smallest

排个序,如果相邻的数大于一就不满足题意

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=110;
int a[N];
int main()
{
    IO;
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        for(int i=0;i<n;i++) cin>>a[i];
        sort(a,a+n);
        bool ok=1;
        for(int i=0;i<n-1;i++)
            if(a[i+1]>a[i]+1) 
            {
                ok=0;
                break;
            }
        if(ok) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

B - Gifts Fixing

礼物最终个数肯定是最小的那个。每个礼物减小到最少的那个,注意可以同时减少两种礼物。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=110;
ll a[N],b[N];
int main()
{
    IO;
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        ll mina=2e9;
        ll minb=2e9;
        for(int i=0;i<n;i++) 
        {
            cin>>a[i];
            mina=min(mina,a[i]);
        }
        for(int i=0;i<n;i++)
        {
            cin>>b[i];
            minb=min(minb,b[i]);
        }
        ll res=0;
        for(int i=0;i<n;i++) res+=max(a[i]-mina,b[i]-minb);
        cout<<res<<endl;
    }
    return 0;
}

C - Boats Competition

枚举+双指针
由于体重非常小,两两配对的体重和也很小,直接暴力枚举两两体重和w。现在问题转化成原序列找出两个数的和等于w,双指针。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=110;
int a[N],n;
int main()
{
    IO;
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        if(n==1) 
        {
            cout<<0<<endl;
            continue;
        }
        sort(a+1,a+n+1);
        int res=0,mx=0;
        for(int w=a[1]+a[2];w<=a[n-1]+a[n];w++)
        {
            int cnt=0;
            for(int i=1,j=n;i<j;i++)
            {
                while(j>i&&a[i]+a[j]>w) j--;
                if(j<=i) break; //注意这点 因为这wa了一次
                if(a[i]+a[j]==w) cnt++,j--;
            }
            res=max(res,cnt);
        }
        cout<<res<<endl;
    }
    return 0;
}

D - Binary String To Subsequences

贪心+二分
贪心:如果该位是1那么找一个末尾是0的序列放进去如果不存在那么新添一组
比如目前有五组0 0 1 1 1分别表示该组最后一个字符是0或者1,如果该位是1我们就二分出最后一个0,pos=2,组别表示变成0 1 1 1 1。如果该位使0我们二分处第一个1的位置,pos=3,组别表示变成0 0 0 1 1,这样操作可以发现组别表示的序列始终是有序的,保证时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
好像有 O ( n ) O(n) O(n)的做法奈何wctl,写题的时候只想到这个做法。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
const int N=200010;
int pos[N],a[N];
int main()
{
    IO;
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        string s;
        cin>>n>>s;
        int cnt=1;
        a[1]=s[0]-'0';
        pos[0]=1;
        for(int i=1;i<n;i++)
        {
            int t=s[i]-'0';
            if(t)
            {
                int l=1,r=cnt;
                while(l<r)
                {
                    int mid=l+r+1>>1;
                    if(a[mid]<=0) l=mid;
                    else r=mid-1;
                }
                if(a[l]==0)
                {
                    a[l]=1;
                    pos[i]=l;
                }
                else 
                {
                    a[++cnt]=1;
                    pos[i]=cnt;
                }
            }
            else
            {
                int l=1,r=cnt;
                while(l<r)
                {
                    int mid=l+r>>1;
                    if(a[mid]>=1) r=mid;
                    else l=mid+1;
                }
                if(a[l]==1)
                {
                    a[l]=0;
                    pos[i]=l;
                }
                else 
                {
                    a[++cnt]=0;
                    pos[i]=cnt;
                }
            }
        }
        cout<<cnt<<endl;
        for(int i=0;i<n;i++) cout<<pos[i]<<" ";
        cout<<endl;
    }
    return 0;
}

这次做了4个题,差一点做出了E1。

E1 - Weights Division (easy version)

我没想到这次我不在意的点就是这题的关键的!!!
先dfs一下把从根节点到每个叶子节点每条边跑的次数记录一下,记作cnt[i],如果该边边权为w[i]如果把这条边砍一半,那么总的路程减小(w[i]-w[i]/2)*cnt[i],显然贪心,用个优先队列(按照减小的多优先级高)维护一下就行了。
坑点:很容易想到按照w[i]*cnt[i]排序,不过这题下取整导致问题不断。(我做的时候想这题应该不会那么麻烦,就图个省事结果。。。)

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100010,M=400010;
int h[N],e[M],ne[M],idx;
ll w[M],limit,res;
int n,d[N],cnt[M];
struct node
{
    ll w;
    int num;
    bool operator <(const node&o)const
    {
        ll sub1=(w-w/2)*num;
        ll sub2=(o.w-o.w/2)*o.num;
        return sub1<sub2;
        //其实也可以直接 return (w+1)/2*num<(o.w+1)/2*o.num; 上去整
    }
};
priority_queue<node> q;

void add(int a,int b,ll c)
{
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
    d[b]++;//统计度数,度数为1是叶子节点
}
int dfs(int u,int fa)
{
    int lcnt=0;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j==fa) continue;
        cnt[i]+=(d[j]==1);
        cnt[i]+=dfs(j,u);
        lcnt+=cnt[i];
    }
    return lcnt;
}
void init()
{
    for(int i=0;i<=n;i++) h[i]=-1,d[i]=0,cnt[i]=0,cnt[i+n]=0;
    idx=res=0;
    while(q.size()) q.pop();
}
int main()
{
    IO;
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>limit;
        init();
        for(int i=1;i<n;i++)
        {
            int a,b;
            ll c;
            cin>>a>>b>>c;
            add(a,b,c);
            add(b,a,c);
        }
        dfs(1,-1);
        for(int i=0;i<2*n-2;i+=2) 
        {
            q.push({w[i],cnt[i]+cnt[i+1]});
            res+=w[i]*(cnt[i]+cnt[i+1]);
        }
        ll ans=0;
        while(res>limit)
        {
            auto t=q.top();
            q.pop();
            q.push({t.w/2,t.num});
            res-=1ll*(t.w-t.w/2)*t.num;
            ans++;
        }
        cout<<ans<<endl;
    }
    return 0;
}

E2 - Weights Division (hard version)

E1砍边都是花费1代价,而E2就是有些边砍一半需要花费1代价有些2代价。显然我们考虑用两个优先队列维护q1,q2q1维护花费1代价的 q2维护花费2代价的。仍然贪心。每次考虑花费1代价砍边,如果发现花两次1代价减小的路程小于直接花费2代价砍边减小的路程,那么就直接花费2代价砍边。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100010,M=200010;
int h[N],e[M],ne[M],idx,cost[M];
ll w[M],limit,res;
int n,d[N],cnt[M];
struct node
{
    ll w;
    int num;
    bool operator <(const node&o)const
    {
        ll sub1=(w-w/2)*num;
        ll sub2=(o.w-o.w/2)*o.num;
        return sub1<sub2;
    }
};
priority_queue<node> q1,q2;
void add(int a,int b,ll c,int coin)
{
    e[idx]=b;
    w[idx]=c;
    cost[idx]=coin;
    ne[idx]=h[a];
    h[a]=idx++;
    d[b]++;
}
int dfs(int u,int fa)
{
    int lcnt=0;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j==fa) continue;
        cnt[i]+=(d[j]==1);
        cnt[i]+=dfs(j,u);
        lcnt+=cnt[i];
    }
    return lcnt;
}
void init()
{
    for(int i=0;i<=n;i++) h[i]=-1,d[i]=0,cnt[i]=0,cnt[i+n]=0;
    idx=res=0;
    while(q1.size()) q1.pop();
    while(q2.size()) q2.pop();
    q1.push({0,0}),q1.push({0,0}),q2.push({0,0});//哨兵,避免很多边界情况。
}
int main()
{
    IO;
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>limit;
        init();
        for(int i=1;i<n;i++)
        {
            int a,b,coin;
            ll c;
            cin>>a>>b>>c>>coin;
            add(a,b,c,coin);
            add(b,a,c,coin);
        }
        dfs(1,-1);
        for(int i=0;i<2*n-2;i+=2) 
        {
            if(cost[i]==1) q1.push({w[i],cnt[i]+cnt[i+1]});
            else q2.push({w[i],cnt[i]+cnt[i+1]});
            res+=w[i]*(cnt[i]+cnt[i+1]);
        }
        ll ans=0;
        while(res>limit)
        {
            auto a=q1.top();q1.pop();
            auto b=q1.top();q1.pop();
            auto c=q2.top();q2.pop();
            ll suba=(a.w-a.w/2)*a.num;
            ll subb=max((a.w/2-a.w/4)*a.num,(b.w-b.w/2)*b.num);
            ll subc=(c.w-c.w/2)*c.num;
            if(res-suba<=limit) 
            {
                ans++;
                break;
            }
            else 
            {
                if(suba+subb<=subc)
                {
                    res-=subc;
                    q1.push(a),q1.push(b);
                    q2.push({c.w/2,c.num});
                    ans+=2;
                }
                else
                {
                    res-=suba;
                    q1.push(b),q1.push({a.w/2,a.num});
                    q2.push(c);
                    ans++;
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

注意:每次只考虑花费1代价砍边
我在写的时候写了一份每次考虑花费2代价砍边。比较两次1代价和一次2代价减小的路程吗,这样贪心其实不正确,非常dt。
下面代码是错误的贪心思路每次考虑花费2代价砍边

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#define debug(x) cout<<#x<<": "<<x<<" "
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N=100010,M=400010;
int h[N],e[M],ne[M],idx,cost[M];
ll w[M],limit,res;
int n,d[N],cnt[M];
struct node
{
    ll w;
    int num;
    bool operator <(const node&o)const
    {
        ll sub1=(w-w/2)*num;
        ll sub2=(o.w-o.w/2)*o.num;
        return sub1<sub2;
    }
};
priority_queue<node> q1,q2;

void add(int a,int b,ll c,int coin)
{
    e[idx]=b;
    w[idx]=c;
    cost[idx]=coin;
    ne[idx]=h[a];
    h[a]=idx++;
    d[b]++;
}
int dfs(int u,int fa)
{
    int lcnt=0;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j==fa) continue;
        cnt[i]+=(d[j]==1);
        cnt[i]+=dfs(j,u);
        lcnt+=cnt[i];
    }
    return lcnt;
}
void init()
{
    for(int i=0;i<=n;i++) h[i]=-1,d[i]=0,cnt[i]=0,cnt[i+n]=0;
    idx=res=0;
    while(q1.size()) q1.pop();
    while(q2.size()) q2.pop();
}
int main()
{
    IO;
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>limit;
        init();
        for(int i=1;i<n;i++)
        {
            int a,b,coin;
            ll c;
            cin>>a>>b>>c>>coin;
            add(a,b,c,coin);
            add(b,a,c,coin);
        }
        dfs(1,-1);
        for(int i=0;i<2*n-2;i+=2) 
        {
            if(cost[i]==1) q1.push({w[i],cnt[i]+cnt[i+1]});
            else q2.push({w[i],cnt[i]+cnt[i+1]});
            res+=w[i]*(cnt[i]+cnt[i+1]);
        }
        q1.push({0,0}),q1.push({0,0}),q2.push({0,0});
        ll ans=0;

        while(res>limit)
        {
            auto a=q1.top();q1.pop();
            auto b=q1.top();q1.pop();
            auto c=q2.top();q2.pop();
            ll suba1=(a.w-a.w/2)*a.num;
            ll suba2=(a.w/2-a.w/4)*a.num;
            ll subb=(b.w-b.w/2)*b.num;
            ll subc=(c.w-c.w/2)*c.num;
            if(res-suba1<=limit) 
            {
                ans++;
                break;
            }
            else if(res-suba1-max(suba2,subb)<=limit||res-subc<=limit) 
            {
                ans+=2;
                break;
            }
            else if(res-suba1-subc<=limit) 
            {
                ans+=3;
                break;
            }
            else 
            {
                if(suba2>subb)
                {
                    if(suba1+suba2>subc)
                    {
                        res-=suba1+suba2;
                        q1.push(b),q1.push({a.w/4,a.num});
                        q2.push(c);
                    }
                    else
                    {
                        res-=subc;
                        q1.push(a),q1.push(b);
                        q2.push({c.w/2,c.num});
                    }
                }
                else
                {
                    if(suba1+subb>subc)
                    {
                        res-=suba1+subb;
                        q1.push({b.w/2,b.num}),q1.push({a.w/2,a.num});
                        q2.push(c);
                    }
                    else
                    {
                        res-=subc;
                        q1.push(a),q1.push(b);
                        q2.push({c.w/2,c.num});
                    }
                }
                ans+=2;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

嘻嘻还是div3能上一点分QvQ,希望今天div2掉少点分(div2日常掉分)-。-,要加油哦~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值