zoukankan      html  css  js  c++  java
  • 憨豆先生卖豆子 ( 线段树 + 思维 + 小技巧 )

    传送门   自家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
    ", ans);
        }
        return 0;
    }
    View Code
    一步一步,永不停息
  • 相关阅读:
    洛谷-P1591 阶乘数码
    洛谷-P1328 生活大爆炸版石头剪刀布
    git的使用
    docker下载命令
    springboot学习笔记
    内部类被实例化才会被加载进内存测试
    springboot环境搭建遇到的问题
    Java多线程的锁机制
    spring JdbcTemplate学习
    多线程循环注意
  • 原文地址:https://www.cnblogs.com/Willems/p/11876069.html
Copyright © 2011-2022 走看看