[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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章