这次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;
}