zoukankan      html  css  js  c++  java
  • CodeForces 371D. Vessels 题解

    发篇题解证明我的博客园还活着......

    前言

    这道题的常规写法大概有:链表,线段树。

    我做的时候什么也没想,就直接线段树干就完了.....因为我没有脑子......

    但是实际上可以用链表做到复杂度 O((n)) 级别。

    具体做法

    • 1.线段树

    线段树做法需要支持的操作为:

    区间覆盖,单点修改,区间查询,单点查询。

    这道题目的难点就在于怎么样维护水一直往下面流。那么我们可以想到二分来判断水会流满哪一些沙漏,然后对于这段区间实行区间覆盖,用一个前缀和(前缀和里面存的是容量的前缀和)) + 区间查询目前水量区间和来辅助二分。最后我们发现会剩下一点水无法注满下一个漏斗,然后采用单点修改,将水注入不能注满的沙漏即可即可。

    对于询问就直接单点查值即可。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    char buf[1 << 22],*p1 = buf,*p2 = buf;
    #define mid ((L[x] + R[x]) >> 1)
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 1 << 21 , stdin),p1 == p2) ? EOF: *p1++)
    inline int read() {
        int x = 0 , flag = 1;
        char ch = getchar();
        for( ; ch > '9' || ch < '0' ; ch = getchar()) if(ch == '-') flag = -1;
        for( ; ch >= '0' && ch <= '9' ; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
        return x * flag;
    }
    
    typedef long long LL;
    const int MAXN = 2e5 + 50;
    int n,m;
    int A[MAXN],S[MAXN];//A存容量,S存容量的前缀和
    
    struct SegmentTree {
        LL sum[MAXN << 2],laz[MAXN << 2],L[MAXN << 2],R[MAXN << 2];
        void build(int x,int l,int r) {
            sum[x] = laz[x] = 0;
            L[x] = l , R[x] = r;
            if(l == r) return ;
            build(x << 1 , l , mid);
            build(x << 1 | 1 , mid + 1 , r);
            return ;
        }
    
        void ad(int x,LL k) {
            laz[x] = 1;
            sum[x] = S[R[x]] - S[L[x] - 1];//全部注满
            return ;
        }
    
        void changepos(int x,int pos,int k) {
            if(pos > n) return ;
            if(L[x] == R[x] && L[x] == pos) {
                sum[x] += k; return;
            }
            pushdown(x);
            if(pos <= mid) changepos(x << 1 , pos , k);
            if(pos  > mid) changepos(x << 1 | 1 , pos , k);
            sum[x] = sum[x << 1] + sum[x << 1 | 1];
            return ;
        }
    
        void change(int x,int l,int r,LL k) {
            if(l > r) return ;
            if(L[x] >= l && R[x] <= r) {
                ad(x,k); return ;
            }
            if(l <= mid) change(x << 1 , l , r , k);
            if(r  > mid) change(x << 1 | 1 , l , r , k);
            sum[x] = sum[x << 1] + sum[x << 1 | 1];
            return ;
        }
    
        void pushdown(int x) {//下传标记
            if(laz[x] == 0) return ;
            ad(x << 1 , laz[x]);
            ad(x << 1 | 1 , laz[x]);laz[x] = 0;
            return ;
        }
    
        int GetVal(int x,int l,int r) {
            if( l > r) return 0;
            int s = 0;
            if(L[x] >= l && R[x] <= r) return sum[x];
            pushdown(x);
            if(l <= mid) s += GetVal(x << 1 , l , r);
            if(r  > mid) s += GetVal(x << 1 | 1 , l , r);
            return s;
        }
    } T;
    
    int check(int u,int val) {//二分找出可以注满的沙漏的区间右端点
        int s = u - 1;//注意这个地方初值要写成u-1
        for(int i = log2(n - u + 1) ; i >= 0 ; i --) {
            int C = s + (1 << i);
            if(C > n) continue;
            if(val >= (S[C] - S[u - 1] - T.GetVal(1,u,C))) s += (1 << i);//二分
        }
        return s;
    }
    
    signed main() {
        n = read();
        for(int i = 1 ; i <= n ; i ++) A[i] = read(),S[i] = S[i - 1] + A[i];
        m = read();
        T.build(1 , 1 , n + 1);
        while(m --) {
            int op = read(),u,x;
            if(op == 1) {
                u = read() , x = read();
                int l = u , r = check(u,x),val = x - (S[r] - S[l - 1] - T.GetVal(1,l,r));
                //val代表的是剩下的水
                T.change(1, l, r, 1);//区间覆盖
                T.changepos(1,r + 1,val);//单点修改
            }
            else {
                u = read();
                cout << T.GetVal(1,u,u) << endl;
            }
        }
        return 0;
    }
    
    • 链表

    大概思路:

    链表里存的是下一个没有被装满的水桶。初始值即为 (nxt[i] = i + 1),当一个水桶被装满的时候,我们每次跳其(nxt[]) 即可,这样子跳下去,直到跳到水没了,然后再更新路径上的 (nxt) 数组。这个方法不难发现均摊下来是 O((n)) 的。代码短了不止一点点,快了不止亿点点。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    char buf[1 << 22],*p1 = buf,*p2 = buf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 1 << 21 , stdin),p1 == p2) ? EOF: *p1++)
    inline int read() {
        int x = 0 , flag = 1;
        char ch = getchar();
        for( ; ch > '9' || ch < '0' ; ch = getchar()) if(ch == '-') flag = -1;
        for( ; ch >= '0' && ch <= '9' ; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
        return x * flag;
    }
    
    typedef long long LL;
    const int MAXN = 2e5 + 50;
    int n,m;
    int A[MAXN],nxt[MAXN],N[MAXN];
    
    signed main() {
        n = read();
        for(int i = 1 ; i <= n ; i ++) A[i] = read(),nxt[i] = i + 1;
        m = read();
        while(m --) {
            int op = read(),u,x;
            if(op == 1) {
                u = read() , x = read();
                int now = u;
                while(nxt[now]) {
                    if(x + N[now] >= A[now]) x -= (A[now] - N[now]), N[now] = A[now];
                    else {N[now] += x ; break;}
                    now = nxt[now];
                }
                int t;
                while(u != now) t = u , u = nxt[u] , nxt[t] = now;
            }
            else {
                u = read();
                cout << N[u] << endl;
            }
        }
        return 0;
    }
    

    总结

    以后一定要动脑子思考问题,不要一上来就打暴力,这个习惯不好......

    By MYCui
  • 相关阅读:
    OSG开源教程(转)
    小组项目工作分配,任务确认
    OSG安装编译
    JAVA开发工具IDEA使用体验
    软件工程结对项目总结
    迭代开发个人总结20160705
    迭代开发个人总结20160704
    迭代开发个人总结20160703
    迭代开发个人总结20160702
    迭代开发个人总结20160701
  • 原文地址:https://www.cnblogs.com/MYCui/p/14394103.html
Copyright © 2011-2022 走看看