码迷,mamicode.com
首页 > 其他好文 > 详细

憨豆先生卖豆子 ( 线段树 + 思维 + 小技巧 )

时间:2019-11-17 14:35:13      阅读:64      评论:0      收藏:0      [点我收藏+]

标签:view   long   输出   onclick   spl   clu   直接   下标   close   

传送门   自家OJ 1313

题意 :

   给你 n个数,让你找一段连续的区间, 使得这段 区间的和 S 除以 P 最大,且S%p <= k;

   问你 最大的 S / P 为多少, 若没有这样的区间, 输出 -1;

 

解:   我们假设, pre[ i ] 为前 i 个数的和, succ[ i ] 为 i ~ n 这些数的和, 即后缀和。

        假设 S 为 所有数的和。

   那么, 假设我们选了一段区间 [ L, R ],  那么这段区间的 数的和即为, S - pre[ L - 1 ] - succ[ R + 1 ];

      那么, 我们要使得 (  S - pre[ L - 1 ]  -  succ[ R + 1 ]  ) % p <= k;

   即  ( S % p - pre[ L - 1 ] % p - succ[ R + 1 ] % p  +  p) % p <= k  咯;

   即  ( S % p - pre[ L - 1 ] % p  + p) % p  <= succ[ R + 1 ] % p <=   ( S % p - pre[ L - 1 ] % p  + p - k + p) % p;

   那我们 搞个线段树,线段树下标 即为 succ[ R + 1 ] % p; 然后下标存的是位置;

   即 满足  succ[ R + 1 ] % p = pos 中, 最大的R; (  因为 R 越大, 加的数越多 );

   那我们枚举  pre[ L - 1] % p; 然后对每个 pre[ L - 1 ] % p 都去线段树 找个 max, 然后更新答案。 即可。

   具体细节,详见代码。

技术图片
#include <bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1e6 + 5;
LL pre[N], succ[N], a[N];
int b[N], c[N];
int T[N << 2];
void built(int rt, int l, int r) { /// 建树
    if(l == r) {
        T[rt] = b[l]; return ;
    }
    int mid = (l + r) >> 1;
    built(rt << 1, l, mid);
    built(rt << 1 | 1, mid + 1, r);
    T[rt] = max(T[rt << 1], T[rt << 1 | 1]);
}
int query(int rt, int l, int r, int L ,int R) { /// 查询区间最大值。
    if(L <= l && r <= R) {
        return T[rt];
    }
    int mid = (l + r) >> 1;
    int ma = 0;
    if(L <= mid) ma = max(ma, query(rt << 1, l, mid, L, R));
    if(R > mid) ma = max(ma, query(rt << 1 | 1, mid + 1, r, L, R));
    return ma;
}
int main() {
    int _; scanf("%d", &_); int cas = 0;
    while(_--) {
        int n, p, k; scanf("%d %d %d", &n, &p, &k);
        for(int i = 0; i <= p * 4; i++) T[i] = 0;
        LL ans = -1; LL S = 0;
        for(int i = 0; i <= p; i++) b[i] = 0, c[i] = INF;
        for(int i = 1; i <= n; i++) {
            scanf("%lld", &a[i]); S += a[i];
            pre[i] = pre[i - 1] + a[i];
            c[pre[i] % p] = min(c[pre[i] % p], i);  /// 维护个 下标为pre[i] % p的最大值 
            if(S % p <= k)  ans=max(ans, S / p); ///前缀和满足条件也可直接更新
        }
        succ[n + 1] = 0;
        for(int i = n; i >= 1; i--) {
            succ[i] = succ[i + 1] + a[i];
            b[succ[i] % p] = max(b[succ[i] % p], i);/// 同理
            if(succ[i] % p <= k) ans=max(ans, succ[i] / p); 
        }
        built(1, 1, p); /// 建树
        for(int i = 1; i < p; i++) {
            if(c[i] == INF) continue;
            int tmp = S % p - i; 
            int R = (tmp + p) % p; int L = (tmp - k + p) % p;
            if(L == 0 || L > R) continue;
            int ma = query(1, 1, p, L, R);
//            cout << L << " " << R << " " << ma << endl;
            if(ma == 0) continue;
            if(ma <= c[i] + 1) continue;
//            puts("come");
            LL sum = S - pre[c[i]] - succ[ma];
            LL q = sum / p;
            ans = max(ans, q);
//            puts("come");
        }
        printf("Case %d: ", ++cas);
        printf("%lld\n", ans);
    }
    return 0;
}
View Code

 

憨豆先生卖豆子 ( 线段树 + 思维 + 小技巧 )

标签:view   long   输出   onclick   spl   clu   直接   下标   close   

原文地址:https://www.cnblogs.com/Willems/p/11876069.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!