首页 > 其他分享 >abc368_G

abc368_G

时间:2024-10-24 21:43:48浏览次数:7  
标签:int tt -- abc368 br ds op

G - Add and Multiply Queries

思路

开始直接用的线段树,写完才意识到是假的

由于题目说答案不会超过\(10^{18}\),所以一个询问区间内的大于2的b的个数不超过64个,这样一个区间内大于2的b的就可以把a分成不超过64个连续的区间,用树状数组维护,b大于2的位置可以用线段树二分或者set的做法来找到(64logn),在这种位置上判断+a和*b哪个选择更好即可

为了防止超时,实际上应该也超不了,用链表+set写复杂度仅为64+logn,具体做法是用链表维护b大于2的位置,查询的时候二分找到第一个b大于二的位置,后面的用链表来找

代码

#define lowbit(x) (x & -x)

int n;
vector<int> a, b;
vector<ll> tr;

void add(int d, int x) {
    for (; d <= n; d += lowbit(d))
        tr[d] += x;
}

ll get(int d) {
    ll res = 0;
    for (; d > 0; d -= lowbit(d))
        res += tr[d];
    return res;
}

ll getd(int l, int r) {
    return get(r) - get(l - 1);
}

void solve() {
    cin >> n;
    a.resize(n + 1), b.resize(n + 1);
    tr.resize(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        add(i, a[i]);
    }
    for (int i = 1; i <= n; i++)
        cin >> b[i];
    set<int> ds;
    vector<int> br(n + 1);
    int last = INF;
    for (int i = n; i >= 1; i--) {
        if (b[i] > 1) {
            ds.insert(i);
            br[i] = last;
            last = i;
        }
    }
    int q;
    cin >> q;
    while (q--) {
        int op, l, r;
        cin >> op >> l >> r;
        if (op == 1) {
            add(l, r - a[l]);
            a[l] = r;
        } else if (op == 2) {
            if (b[l] < 2 && r > 1) {
                auto tt = ds.lower_bound(l);
                if (tt != ds.end())
                    br[l] = *tt;
                else
                    br[l] = INF;
                if (tt != ds.begin()) {
                    br[*--tt] = l;
                }
                ds.insert(l);
            } else if (b[l] > 1 && r < 2) {
                auto tt = ds.lower_bound(l);
                auto ft = tt;
                if (tt != ds.begin())
                    br[*--tt] = br[l];
                ds.erase(ft);
            }
            b[l] = r;
        } else {
            ll v = a[l++];
            if (l > r)
                cout << v << endl;
            else {
                auto tt = ds.lower_bound(l);
                int d = (tt == ds.end() ? INF : *tt);
                while (d <= r) {
                    if (d > l) {
                        v += getd(l, d - 1);
                    }
                    v + a[d] >= v* b[d] ? (v += a[d]) : (v *= b[d]);
                    l = d + 1;
                    d = br[d];
                }
                if (l <= r) {
                    v += getd(l, r);
                }
                cout << v << endl;
            }
        }
    }
}

标签:int,tt,--,abc368,br,ds,op
From: https://www.cnblogs.com/mgnisme/p/18501388

相关文章

  • AtCoder Beginner Contest 368(ABC368)
    [ABC369C]CountArithmeticSubarrays题意:判断有多少个区间是等差数列(不能重排)。\(1\len\times10^5\)。思路:赛时看错题了,以为这个区间可以重排,卡了8min,小丑了。首先容易注意到,对于一个区间\([l,r]\),若其是等差数列,则这个区间的子区间\([l',r']\)肯定也是子序列,造成......
  • ABC368
    Alink先输出后面,在输出前面。神奇的代码#include<bits/stdc++.h>usingnamespacestd;intn,k;inta[105];signedmain(){ cin>>n>>k; for(inti=1;i<=n;++i){ cin>>a[i]; if(i>=n-k+1)cout<<a[i]<<"......
  • ABC368
     D树从叶子到根,对于某个点,如果其子树不存在需要的点,那么这个点和它的父亲所连的边,自然不需要,否则需要。有一个问题,比如需要点2、4、5,那么点1和点2所连的边也算进去了。实际上,到了它们的LCS(最大公共祖先)后,这些边就不用算了。用一个变量统计当前遍历过多少需要的点,如果所有需要......
  • ABC368G
    前言最简单的一次,终于AK了ABC,纪念一下。思路看到题目中有一句加粗的话入力で与えられるタイプ$3$のクエリの答えは$10^{18}$以下であることが保証されています。翻译出来是对于所有操作\(3\),答案不超过\(10^{18}\)。首先,\(a_i\)一定不会是\(0\),考虑一种情况,......
  • AtCoder Beginner Contest 368(ABC368)
    [ABC368F]DividingGame题意:有\(n\)堆石子,第\(i\)堆有\(a_i\)颗石子,每次可以拿走任意一堆石子数量任何数量的棋子,但是要保证拿走之后该堆的石子数量为原来的约数(不能不拿)。问是先手必胜还是后手必胜。\(n,a_i\le10^5\)。思路:发现与Nim游戏类似,且全局信息公开,状态......
  • ABC368
    A.Cut模拟代码实现#include<bits/stdc++.h>#definerep(i,n)for(inti=0;i<(n);++i)usingnamespacestd;intmain(){intn,k;cin>>n>>k;vector<int>a(n);rep(i,n)cin>>a[i];r......
  • abc368 题解
    切了ABCDF,G赛后1min切了(恼比赛链接:https://atcoder.jp/contests/abc368A-Cut题意:给定一个长度为\(n\)的序列,先输出后\(k\)个数,在输出前\(n-k\)个数。思路:按题意模拟即可。代码:https://atcoder.jp/contests/abc368/submissions/57030066B-Decrease2maxel......