• 题目: 妄想集合

  • 题意:给出\(n(1\leq n \leq 10^5)\)个可重集合,开始每个集合给出一个数\(a_i(1\leq a_i\leq 10^9)\),有\(m(1\leq m \leq 10^5)\)次操作。共有两种操作:

    \(\text{Quant l r x}\):往编号在\(l\sim r\) 的每个集合中加入一个数 x。

    \(\text{Ask l r}\):询问能否从 \(l\sim r\) 的集合中取出三个数使得他们能作为边长组成一个三角形(即最小两个和要大于最大的)。

    对于每个 \(\text{Ask}\) 操作,输出一行表示答案,能则输出 YES ,否则输出 NO。

  • 思路:线段树、思维、剪枝。

  • 解析:若希望一些数中找不到任意一组数(\(1\sim 10^9\))能组成一个三角形,那么又希望这些数的数量尽可能长的情况下只有斐波那契数列正好是它的极限,而斐波那契数列第四十五项已经超过\(10^9\),所以我们可以特判询问的一个区间的所有集合中的元素总个数大于或等于45时肯定能找到一组能组成三角形的数,否则可以直接暴力枚举这个区间所有集合中的元素,最多也不超过45个(枚举前从大到小排个序,原因不说了)。

    找到突破口之后就是利用线段树维护一个区间集合的元素个数,这里还需要加一个小标记\(tag\),0:表示该区间集合个数 \(\leq 45\),反之...,这个小标记是为了在区间修改剪枝而用的,因为区间修改一般而言都要懒标记来降低时间复杂度嘛,所以这里可以通过一个剪枝来判断是否还有必要进行对该子树的修改。这种线段树的剪枝第一次遇见,相当于在pushup中每次将子节点的标记进行求交,判断是否存在一棵子数的区间所有集合的元素总个数大于或等于45,若是这样,它的父节点在下一次被进行更新操作时根本就不需要继续往下更新。

  • 代码:

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int N = 1e5 + 5;
    const int M = 45;
    int n, m;
    vector<int> v[N];
    struct Node
    {
        int l, r;
        int cnt = 0; //该区间集合的结点总个数
        int tag = 0; //0:表示该区间集合个数 < M
    }tr[4 * N];
    
    void pushup(int u)
    {
        tr[u].cnt = tr[u << 1].cnt + tr[u << 1 | 1].cnt;
        tr[u].tag = tr[u << 1].tag && tr[u << 1 | 1].tag; //最终判断父节点的tag即可
    }
    
    void build(int u, int l, int r)
    {
        tr[u] = {l, r};
        if(l == r)
        {
            if(v[l][0])
            {
                tr[u].cnt = 1;
                tr[u].tag = 0;
            }
            return;
        }
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
    
    int query(int u, int l, int r)
    {
        if(l <= tr[u].l && r >= tr[u].r)
            return tr[u].cnt;
        int mid = tr[u].l + tr[u].r >> 1;
        int cnt = 0;
        if(l <= mid) cnt = query(u << 1, l, r);
        if(r > mid) cnt += query(u << 1 | 1, l, r);
        return cnt;
    }
    
    void update(int u, int l, int r, int x) //区间修改 但部分可能仅仅是单点修改甚至不修改
    {
        if(tr[u].l >= l && tr[u].r <= r && tr[u].tag) //该区间所有集合的元素个数均大于M
            return;
        if(tr[u].l == tr[u].r)
        {
            tr[u].cnt ++;
            v[tr[u].l].push_back(x); //单点修改
            if(tr[u].cnt >= M) tr[u].tag = 1; //判断该集合元素个数是否>=45
            return;
        }
        int mid = tr[u].l + tr[u].r >> 1;
        if(l <= mid) update(u << 1, l, r, x);
        if(r > mid) update(u << 1 | 1, l, r, x);
        pushup(u);
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)
        {
            int x;
            scanf("%d", &x);
            v[i].push_back(x);
        }
        build(1, 1, n);
        while(m --)
        {
            char op[10];
            int l, r, x;
            scanf("%s", op);
            if(*op == 'Q')
            {
                scanf("%d%d%d", &l, &r, &x);
                update(1, l, r, x);
            }
            else
            {
                scanf("%d%d", &l, &r);
                int cnt = query(1, l, r);
                if(cnt >= 45) printf("YES\n");
                else if(cnt < 3) printf("NO\n");
                else
                {
                    vector<int> temp;
                    temp.clear();
                    int flag = 0;
                    for(int i = l; i <= r; i++)
                    {
                        for(int j = 0; j < v[i].size(); j++)
                        {
                            temp.push_back(v[i][j]);
                        }
                    }
                    sort(temp.begin(), temp.end());
                    for(int i = 0; i < temp.size() - 2; i++)
                    {
                        int a = temp[i], b = temp[i + 1], c = temp[i + 2];
                        if(a + b > c)
                        {
                            flag = 1;
                            break;
                        }
                    }
                    if(flag) printf("YES\n");
                    else printf("NO\n");
                }
            }
        }
        return 0 ;
    }