zoukankan      html  css  js  c++  java
  • HDU6521 Party (线段树思维)

    1.首先明确这道题想要求取的是在l-r中新认识的人的对数

    如果想要知道新认识的人是多少,那么我们可能会考虑到上次认识的人的左右边界。并且可以想象到的是,在一个区间中,右边的人新认识的左边的人,其实就是左边的人新认识的右边的人

    如果重复的计算,那么最后要/2。所以我们可以直接考虑有边界

    2.有一个性质可以通过观察得到,也就是越往右的人的右边界一定大于等于左边的右边界,因为每次都是连续的一段区间,左边的人不可能绕过右边的人去认识更右边的人。所以这个维护的值是递增的

    那么每次新认识的人其实就是找到最右边的一个有边界小于r的点pos,答案就是(r-l+1)*r-从l-pos的r相加,也就是前面一段中每个离右边界的人数之和

    这样就发现了一个区间求和操作,之后我们要更改掉现有的有边界,也就是将l-pos这段的有边界都设置为r,这里有个区间修改操作。同时还需要一个函数来计算pos的位置

    这样就能看出线段树非常擅长这些

    对于区间问题,需要仔细想可以接受复杂度内的一些性质。以后还要多多锻炼,这个是向网上博客大佬学习后才发现的做法。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<map>
    #include<algorithm>
    #include<queue>
    #define ull unsigned long long
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=5e5+10;
    struct node{
        int l,r;
        ll sign;
        ll lazy;
        ll sum;
    }tr[N<<2];
    void pushup(int u){
        tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;//边界的r和
        tr[u].sign=tr[u<<1|1].sign;//最右边界的更新,肯定是由右边更新而来
    }
    void build(int u,int l,int r){
        if(l==r){
            tr[u]=node{l,r,l,0,l};
        }
        else{
            int mid=l+r>>1;
            tr[u]={l,r};
            build(u<<1,l,mid);
            build(u<<1|1,mid+1,r);
            pushup(u);
        }
    }
    void pushdown(int u){
        tr[u<<1].lazy=tr[u<<1|1].lazy=tr[u].lazy;
        tr[u<<1].sign=tr[u<<1|1].sign=tr[u].lazy;
        tr[u<<1].sum=(tr[u<<1].r-tr[u<<1].l+1)*tr[u].lazy;
        tr[u<<1|1].sum=(tr[u<<1|1].r-tr[u<<1|1].l+1)*tr[u].lazy;
        tr[u].lazy=0;
    }
    void modify(int u,int l,int r,ll x){
        if(tr[u].l>=l&&tr[u].r<=r){
            tr[u].sign=x;
            tr[u].lazy=x;
            tr[u].sum=(tr[u].r-tr[u].l+1)*x;//对于范围内直接修改
            return ;
        }
        if(tr[u].lazy!=0)
            pushdown(u);
        int mid=tr[u].r+tr[u].l>>1;
        if(l<=mid)
            modify(u<<1,l,r,x);
        if(r>mid)
            modify(u<<1|1,l,r,x);
        pushup(u);
    }
    int querypos(int u,int x){
        if(tr[u].l==tr[u].r){
            if(tr[u].sign<=x)
                return tr[u].l;
            else
                return -1;//可能就没有满足条件的
        }
        if(tr[u].lazy)
        pushdown(u);
        if(tr[u<<1].sign<=x) //当左边满足条件,就要对两边的取max
            return max((int)tr[u<<1].r,querypos(u<<1|1,x));
        else{
            return querypos(u<<1,x);//不然就在左边
        }
    }
    ll query(int u,int l,int r){
        if(tr[u].l>=l&&tr[u].r<=r)
            return tr[u].sum;
        int mid=tr[u].l+tr[u].r>>1;
        if(tr[u].lazy)
        pushdown(u);
        ll ans=0;
        if(l<=mid)
            ans=query(u<<1,l,r);
        if(r>mid)
            ans+=query(u<<1|1,l,r);
        return ans;
    }
    int main(){
        int n,m;
        while(cin>>n>>m){
            build(1,1,n);
            while(m--){
                int l,r;
                scanf("%d%d",&l,&r);
                ll ans=0;
                int pos=querypos(1,r);
                if(pos<l)
                    ans=0;
                else{
                     ans=(ll)(pos-l+1)*r-query(1,l,pos);
                     modify(1,l,pos,r);
                }
                printf("%lld
    ",ans);
    
            }
        }
    }
    View Code
  • 相关阅读:
    poj 1789 每个字符串不同的字母数代表两个结点间的权值 (MST)
    poj 1251 poj 1258 hdu 1863 poj 1287 poj 2421 hdu 1233 最小生成树模板题
    poj 1631 最多能有多少条不交叉的线 最大非降子序列 (LIS)
    hdu 5256 最少修改多少个数 能使原数列严格递增 (LIS)
    hdu 1025 上面n个点与下面n个点对应连线 求最多能连有多少条不相交的线 (LIS)
    Gym 100512F Funny Game (博弈+数论)
    UVa 12714 Two Points Revisited (水题,计算几何)
    UVa 12717 Fiasco (BFS模拟)
    UVa 12718 Dromicpalin Substrings (暴力)
    UVa 12716 && UVaLive 6657 GCD XOR (数论)
  • 原文地址:https://www.cnblogs.com/ctyakwf/p/12616499.html
Copyright © 2011-2022 走看看