4.11学习记录

C. Water the Trees
难度 1700
二分答案,算是二分答案里比较复杂的

根据题意
1.我们知道在一段时间里加高2的次数和加高1的次数是固定,所以我们可以选择长高2的时候应该优先考虑。
2.每个树需要长高的空间就是目标高度和这棵树高度的差值
3.目标高度可能是数组中最高的或者最高的+1(我求稳按照最高+3来的)

#include <bits/stdc++.h>
using namespace std;
#define int long long
int tree[300000+100];
int n;
bool check(int day,int maxx)
{
    int sum=0;
    int even=day/2;
    int odd=(day+1)/2;
    for(int i=1;i<=n;i++)
    {
        int dis=maxx-tree[i];
        sum+=dis;
        if(dis<=even*2)
        {
            even-=dis/2;
            odd-=dis%2;
        }
        else
        {
            dis-=even*2;
            even=0;
            odd-=dis;
        }
        if(odd<0)
        {
            return false;
        }
    }
    even=day/2;
    odd=(day+1)/2;
    if(sum>even*2+odd)
        return false;
    return true;
}
signed main()
{
    int t;
    for(cin>>t;t;t--)
    {
        cin>>n;
        int ans=1e17+10;
        for(int i=1;i<=n;i++)
        {
            cin>>tree[i];
        }
        sort(tree+1,tree+n+1);
        for(int i=tree[n];i<=tree[n]+3;i++)
        {
            int l=0,r=1e17+7;
            while(l<=r)
            {
                int mid=(l+r)/2;
                if(check(mid,i))
                {
                    r=mid-1;
                }
                else
                {
                    l=mid+1;
                }
            }
            ans=min(ans,l);
        }
        cout<<ans<<endl;
        for(int i=1;i<=n;i++)
        {
            tree[i]=0;
        }
    }
}

D. Progressions Covering
难度 1900
贪心好题,线段树好题

题意:给定长度为n的数组,每次可以选择长度为k的一段区间减去长度为k的升序排列(1 2 3 4…n)
问最多用几次减去排列的操作能够让整个数组都小于等于0

思路一:从前往后减,我们发现第一个元素始终只能减去1,所以开始必定要先减掉b[1]个全排列,然后从前往后去找大于1的数,但是这种思路会发现在第一步减掉b[1]个全排列之后就会很难操作,我放弃了

思路二:从后往前减,如果从后往前,那么最后一个元素减的值每次为k,最多减(b[n]-sum)/k+((b[n]-sum)%k!=0)次全排列,对于第n位减掉的总值是次数*k,每次往后一位减掉的总值少k,当碰到减掉的总值少于这一位的数值时再次进行(b[i]-sum)/k+((b[i]-sum)%k!=0)次全排列,此时的sum就是之前操作对于第i位减掉的总值,然而这样还需要分1到k-1和k到n。

具体看代码。

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,b[600005],k,op,sum,ans,a[600005];
signed main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>b[i];
    }
    int ans=0;
    for(int i=n;i>=k;i--)
    {
        if(b[i]>sum)
        {
            a[i]=(b[i]-sum)/k+((b[i]-sum)%k!=0);
            sum+=k*a[i];
            op+=a[i];
            ans+=a[i];
        }
        sum-=op;
        op-=a[i+k-1];
    }
    for(int i=k-1;i>=1;i--)
    {
        if(b[i]>sum)
        {
            a[i]=(b[i]-sum)/i+((b[i]-sum)%i!=0);
            sum+=a[i]*i;
            op+=a[i];
            ans+=a[i];
        }
        sum-=op;
        op-=a[i+k-1];
    }
    cout<<ans<<endl;
	return 0;
}

P1650 田忌赛马
难度:提高
区间DP好题,贪心好题。
练练手罢

#include <bits/stdc++.h>
using namespace std;
int a[3000];
int b[3000];
bool cmp(int a,int b)
{
    return a>b;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int j=1;j<=n;j++)
    {
        cin>>b[j];
    }
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1,cmp);
    int fa=1;
    int ta=n;
    int fb=1;
    int tb=n;
    int ans=0;
    while(n--)
    {
        if(a[fa]>b[fb])
        {
            fa++;
            fb++;
            ans+=200;
        }
        else if(a[fa]<b[fb])
        {
            fb++;
            ta--;
            ans-=200;
        }
        else
        {
            if(a[ta]>b[tb])
            {
                ta--;
                tb--;
                ans+=200;
            }
            else
            {
                if(a[ta]<b[fb])
                ans-=200;
                ta--;
                fb++;
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

B. Getting Zero
这题我的做法感觉是非正解的,,但是没任何优化下跑了46m感觉还好
难度:1300
有趣的数学题
思路:预处理+寻找最小值

#include <iostream>
#include <algorithm>
using namespace std;
const int mod=32768;
int a[32768+100];
int num[32768+100];
int ans[32768+100];
signed main()
{
    for(int i=1;i<=32768;i++)
    {
        int temp=i;
        int cnt=0;
        while(temp)
        {
            cnt++;
            temp=(temp*2)%mod;
        }
        a[i]=cnt;
    }
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>num[i];
        ans[i]=a[num[i]];
        for(int j=1;j<=15;j++)
        {
            if(num[i]+j>32768)
               break;
            ans[i]=min(ans[i],a[num[i]+j]+j);
        }
        ans[i]=min(ans[i],32768-num[i]);
    }
    for(int i=1;i<=n;i++)
    {
        cout<<ans[i]<<" ";
    }
    cout<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值