[CSP-S 2019 day2 T2] 划分

题面

题解

  CSP赛场上能请教别人吗  

在这道题中,我看到了一个很敏感又很熟悉的东西——平方!

这意味着,可以推出一些结论,使这道题几乎可以边输入边解决。

自己在脑子里动态一下就知道,像这种总和一定、代价为平方的模式一眼就可以看出这个明显的结论:最大的段最小 !

 (可惜笔者做到后来把它忘了) 

于是,我们就可以把一个总和一定的一个序列,使它的最后一段的和最小。

令f[i]为1~i中,最后一段最靠右的可能的左端点 - 1,

所以,

f[i] = max{ (j < i && sum[i] - sum[j] >= f[j]) ? j : 0 } ;

单调队列学得好的大佬可以用比较灵活的单调栈去实现,

然后再用一个卡常压位高精就行。

CODE

可惜笔者卡常不会

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
inline int read() {
    int f = 1,x = 0;char s = getchar();
    while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();}
    while(s >= '0' && s <= '9') {x = x * 10 + s - '0';s = getchar();}
    return x * f;
}
struct bignum{
    LL a[8];
    int le;
    bignum(){memset(a,0,sizeof(a));le = 1;}
    bignum(LL x){a[4] = 0;a[3] = 0;a[2] = x / 1e9;a[1] = x % ((LL)1e9),le = 1;}
    void operator = (LL x) {
        a[4] = 0;a[3] = 0;a[2] = x / 1e9;a[1] = x % ((LL)1e9),le = 1;
    }
};
bignum operator * (bignum x,bignum y) {
    bignum z;
    for(int i = 1;i <= 3;i ++) {
        LL m = 0;
        for(int j = 1;i + j - 1 <= 4;j ++) {
            z.a[i + j - 1] += x.a[i] * y.a[j] + m;
            m = z.a[i + j - 1] / 1e9;
            z.a[i + j - 1] %= ((LL)1e9);
        }
    }
    return z;
}
bignum operator + (bignum x,bignum y) {
    bignum z;
    LL m = 0;
    for(int i = 1;i <= 4;i ++) {
        z.a[i] = x.a[i] + y.a[i] + m;
        m = z.a[i] / 1e9;
        z.a[i] %= ((LL)1e9);
    }
    return z;
}
LL mod = 1073741824;
int n,m,i,j,s,o,k;
LL a[40000005];
LL sum[40000005];
int f[40000005];
int q[40000005],tail,head;
LL min(LL a,LL b) {
    return a < b ? a : b;
}
inline LL js(int x) {
    return sum[x] + sum[x] - sum[f[x]];
}
int main() {
//  freopen("partition.in","r",stdin);
//  freopen("partition.out","w",stdout);
    n = read();
    tail = head = 1;
    q[1] = 0;
    bool T = read();
    if(T) {
        LL x = read(),y = read(),z = read();
        a[1] = read();a[2] = read();m = read();
        for(register int i = 3;i <= n;i ++) a[i] = (a[i - 1] * x % mod + y * a[i - 2] % mod + z) % mod;
        int pp = 0;
        for(register int i = 1;i <= m;i ++) {
            int p = read();
            LL l = read(),r = read();
            for(register int j = pp + 1;j <= p;j ++) {
                (a[j] %= (r - l + 1ll)) += l;sum[j] = sum[j - 1] + a[j];
                while(tail <= head && js(q[head]) >= js(j - 1)) head --;
                q[++head] = j - 1;
                while(tail < head && js(q[tail + 1]) <= sum[j]) tail ++;
                f[j] = q[tail];
            }
            pp = p;
        }
    }
    else {
        for(register int i = 1;i <= n;i ++) {
            a[i] = read();sum[i] = sum[i - 1] + a[i];
            while(tail <= head && js(q[head]) >= js(i - 1)) head --;
            q[++head] = i - 1;
            while(tail < head && js(q[tail + 1]) <= sum[i]) tail ++;
            f[i] = q[tail];
        }
    }
    register int p = n;
//  cout<<"ok"<<endl;
    bignum ans = 0ll;
//  __int128_t ans2 = 0;
    while(p) {
        ans = (ans + (bignum(sum[p] - sum[f[p]]) * bignum(sum[p] - sum[f[p]])));
//      ans2 += ((sum[p] - sum[f[p]]) * (sum[p] - sum[f[p]]));
//      printf("[%d,%d]\n",f[p] + 1,p);
        p = f[p];
    }
    int le = 4;
    while(ans.a[le] == 0 && le > 1) le --;
    printf("%lld",ans.a[le]);
    for(int i = le - 1;i > 0;i --) printf("%09lld",ans.a[i]);
    printf("\n");
    return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值