Codeforces Round #645 (Div. 2)

31 篇文章 1 订阅
9 篇文章 0 订阅

这次DIV2 有点难搞,这里补一下CD题,感觉自己做难题的速度还是不太行

传送门

C题解题思路:
  • 首先C题可以自己先瞎推推,首先要写一下,不能盲目的猜,因为路径和里有相同的数(自己举例一下就好)

  • 然后其中可以满足的数目其实就是路途上的数的和的 最大值 - 最小值 + 1, 这个应该好理解吧。

  • 然后我们求最大值最小值的差值即可,最大值是先向下走,再向右走,最小值是先向右走,再向下走(这个规律还是比较容易发现的),如果想单纯的直接累加求肯定是爆掉的,然后找他们之间的规律。

  • 首先我们看下图,图有点丑,hhh
    在这里插入图片描述

  • 这里我们从1 到 18 ,我们的最小值 是 2 4 7 12 ,最大值是 3 6 9 13 (这里不算首尾),我们可以发现,2 3 的差值是1 , 4 6 的差值是2, 7 9的差值也是 2, 12 13 的差值是 1,所以4个数他们的差值是 1 2 2 1,那么我们可以多画些来找规律,发现都是 1 2 3 … n n n …n -1, … 3 2 1 这种形式,其中中间的n这个是随便写的,个数按照输入的值来确定,我们可以推出上升的个数(没到最大值)就是 m = min(x2 - x1 ,y2 - y1) - 1 ,然后中间的数就是 x + y - 2 - 1 - 2 * m, x + y - 1是总数目,然后 -2 是去掉首尾,所以这就是中间最大值的个数,也就是 min(x2 - x1 ,y2 - y1) - 1的个数,因此我们按照这种计算即可。

  • 然后解释一下为什么是差一个差几个,因为我们看2 3 ,他们是连续的是差1,然后4 6 是中间隔了一个,所以差2,然后7到9也是2,按照这个相同的道理即可。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 100010;

int a[N];

int main(){
    int t;
    scanf("%d",&t);

    while(t--){
        long long x1,x2,y1,y2;
        scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
        ll x = x2 - x1 + 1;
        ll y = y2 - y1 + 1;
        if (x == 1 || y == 1){
            puts("1");
        }
        else{
            ll m = min(x, y) - 1;
            ll num = x + y - 3;
            ll res = (m + 1) * m;
            res += (num - m * 2) * m + 1;
            printf("%lld\n",res);
        }
    }
    return 0;
}


D题解题思路:
  • 这里首先要知道一个贪心的知识:就是我们选的天数的最后结尾一定是会在一个月的结尾处
  • 首先来瞎推(证明)下这个结论吧:
  • 有4个月份吧,每个月份的天数 2 4 3 3, 那么我们住5天,手算的话一定是13个能量值是最大的(用能量值表示吧),我们会选 3 4 1 2 3,这里是最大的,如果我们进入下一个月,那么会变为4 1 2 3 1,这里少了 2, 如果再加,还会少2,按照这个道理推下去,就OK了。
  • 知道这个结论很重要,这样问题就简单多了。
  • 看数据范围1e5,那么nlogn,很容易想到二分(一开始就想到贪心+二分,但是不太会写,hhh还是太菜了),然后我们排着枚举每一个点,也就是2 * n,这里为什么是2 * n呢, 因为他是一个循环,题目中说不一定在一年内,但是不会超过2年,与其写成一个环,不如让他整成2倍的长度,这样也省事。
for (int  i = 1; i <= n; i++){
    scanf("%lld",&a[i]);
    a[n + i] = a[i];
}
  • 这里我们需要对数据处理,一个是处理每一个月之前的前缀和,就是总共有多少天,为了判断x的位置,第二个是这个月有多少能量值,用m代表这个月的天数的话,那么他的能量值就是(m + 1) * m / 2,我们开一个数组记录下他的前缀和,这个就是能量值的前缀和。
for (int i = 1; i <= 2 * n; i ++){
   	ans[i] = ans[i - 1] + a[i]; // 计算数目
   	sum[i] = sum[i - 1] + (a[i] + 1) * a[i] / 2;  // 计算能量值
}
    
  • 枚举就是枚举最后的月份,利用上面的知识,我们只需要前面的位置即可,所以2分即可,我们二分找到那个>= x 的那个月份,然后找到那个月份,我们首先计算出我们剩下多少天,i是截止的月份,l是找到的最前面的月份,temp = x - (ans[i] - ans[l]) 就是剩下的天数(ans就是记录天数的),然后我们先计算所以的,就是直接加上这个天数的所有能量值,然后根据剩余的天数,我们将多余的能量减去即可(比如 1 2 3 4,我们先加上1 + 2 + 3 +4 ,如果他temp = 2 ,我们只需减去前面的1 + 2即可)
ll temp = x - (ans[i] - ans[l]);
ll cnt = sum[i] - sum[l] + a[l] * (a[l] + 1) / 2;  // 计算全部
ll m = a[l] - temp; // 是否有剩余
cnt -= m *(m + 1) / 2;
  • 然后就排着寻找最大值即可,这里注意下long long , 1e11的数据,会卡很多人吧,hhh
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int N = 400010;

ll ans[N], sum[N], a[N];

int main(){
    ll n, x;
    scanf("%lld%lld",&n,&x);
    for (int  i = 1; i <= n; i++){
        scanf("%lld",&a[i]);
        a[n + i] = a[i];
    }

    for (int i = 1; i <= 2 * n; i ++){
        ans[i] = ans[i - 1] + a[i];
        sum[i] = sum[i - 1] + (a[i] + 1) * a[i] / 2; 
    }

    ll res = 0;
    for (int i = 1; i <= 2 * n; i++){
        if (x > ans[i]) continue;

        int l = 0, r = i;
        

        while(l < r){
            int mid = l + r >> 1;
            if (ans[i] - ans[mid] <= x) r = mid;
            else l = mid + 1; 
        }
        ll temp = x - (ans[i] - ans[l]);

        ll cnt = sum[i] - sum[l] + a[l] * (a[l] + 1) / 2;  // 计算全部

        ll m = a[l] - temp; // 是否有剩余

        cnt -= m *(m + 1) / 2;
        
        res = max(res,cnt);
    }
    printf("%lld\n",res);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值