• AtCoder Regular Contest 068 E


    E - Snuke Line
    Time limit : 2sec / Memory limit : 256MB

    Score : 700 points

    Problem Statement
    Snuke has decided to play a game, where the player runs a railway company. There are M+1 stations on Snuke Line, numbered 0 through M. A train on Snuke Line stops at station 0 and every d-th station thereafter, where d is a predetermined constant for each train. For example, if d=3, the train stops at station 0, 3, 6, 9, and so forth.

    There are N kinds of souvenirs sold in areas around Snuke Line. The i-th kind of souvenirs can be purchased when the train stops at one of the following stations: stations li, li+1, li+2, …, ri.

    There are M values of d, the interval between two stops, for trains on Snuke Line: 1, 2, 3, …, M. For each of these M values, find the number of the kinds of souvenirs that can be purchased if one takes a train with that value of d at station 0. Here, assume that it is not allowed to change trains.

    Constraints
    1≦N≦3×105
    1≦M≦105
    1≦li≦ri≦M
    Input
    The input is given from Standard Input in the following format:

    N M
    l1 r1
    :
    lN rN
    Output
    Print the answer in M lines. The i-th line should contain the maximum number of the kinds of souvenirs that can be purchased if one takes a train stopping every i-th station.

    Sample Input 1
    3 3
    1 2
    2 3
    3 3
    Sample Output 1
    3
    2
    2
    If one takes a train stopping every station, three kinds of souvenirs can be purchased: kind 1, 2 and 3.
    If one takes a train stopping every second station, two kinds of souvenirs can be purchased: kind 1 and 2.
    If one takes a train stopping every third station, two kinds of souvenirs can be purchased: kind 2 and 3.
    Sample Input 2
    7 9
    1 7
    5 9
    5 7
    5 9
    1 1
    6 8
    3 4
    Sample Output 2
    7
    6
    6
    5
    4
    5
    5
    3
    2

    题意:列车在长度为M的街道上行走,有M个站,现在列车将会每隔d站停一下,如d=3时,将在0,3,6,9…站停,d的值是 1 ~ M,现在有N种礼物,每种礼物可以在Li ~ Ri站买到,问d为不同值时,都可以买到多少种礼物。输出d从1~M不同停站方式所能买到的礼物种类。

    实际上是对于每个站点被不同礼物覆盖了多少次的单点查询,以及对每个礼物的区间更新,分别在哪些站可以买到。更新可能只有nlog,但查询,对于每个站点都查询一次,对于一个d就有n/d次logn的查询,然而这个d 还有m个,相当于d=1时就有nlogn次查询,更不用说d=2,3…时所有更新的求和。

    可以想到,一个礼物,若其覆盖的区间长度,大于d,说明肯定跨越了大于1个站的范围。因此对于这些礼物,只需判断其长度即可得知是否能买到,因此,对于d,所有区间范围大于d的礼物都是可以买到的,这些不用查询,直接加上。

    因此一开始输入时,可以将礼物按区间长度分类。当计算d能买到的礼物数量时,所有小于d的礼物被更新到线段树或树状数组上,用于查询每个站点被覆盖次数。从间隔小的d开始查询,需要做区间更新的礼物将一开始很少,因为大于d的区间非常多,而此时直接加上这些礼物个数即可。然后更新小于d的区间将被记录到树上,并且从头至尾只会记录一次,后期可以直接用这些小区间的覆盖,而那些大于d的数量,初始是n,在每次遍历d时,会直接减掉小于d的区间。

    因此并不会因为又从树上查询加,又通过直接加大于d的个数而重复加礼物,因为两种情况并没有重叠。

    线段树区间更新单点查询

    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    #include<vector>
    using namespace std;
    const int maxn=1e5+10;
    struct num
    {
        int l,r;
    } a[3*maxn];
    struct node
    {
        int l,r,sum,mark;
    } tre[maxn<<2];
    void build(int l,int r,int rt)
    {
        tre[rt].l=l;
        tre[rt].r=r;
        tre[rt].mark=0;
        if(l==r)
        {
            tre[rt].sum=0;
            return ;
        }
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        tre[rt].sum=tre[rt<<1].sum+tre[rt<<1|1].sum;
        return ;
    }
    void pushdown(int rt)
    {
        if(tre[rt].mark)
        {
            tre[rt<<1].sum+=(tre[rt<<1].r-tre[rt<<1].l+1)*tre[rt].mark;
            tre[rt<<1|1].sum+=(tre[rt<<1|1].r-tre[rt<<1|1].l+1)*tre[rt].mark;
            tre[rt<<1].mark+=tre[rt].mark;
            tre[rt<<1|1].mark+=tre[rt].mark;
            tre[rt].mark=0;
        }
    }
    void update(int l,int r,int val,int rt)
    {
        if(tre[rt].l>=l&&tre[rt].r<=r)
        {
            tre[rt].sum+=(tre[rt].r-tre[rt].l+1)*val;
            tre[rt].mark+=val;
        }
        else
        {
            pushdown(rt);
            int mid=(tre[rt].l+tre[rt].r)>>1;
            if(l>mid) update(l,r,val,rt<<1|1);
            else if(r<=mid) update(l,r,val,rt<<1);
            else
            {
                update(l,r,val,rt<<1);
                update(l,r,val,rt<<1|1);
            }
            tre[rt].sum=tre[rt<<1].sum+tre[rt<<1|1].sum;
        }
    }
    int query(int pos,int rt)
    {
        if(tre[rt].l==pos&&tre[rt].r==pos) return tre[rt].sum;
        else
        {
            pushdown(rt);
            int mid=(tre[rt].l+tre[rt].r)>>1;
            if(pos>mid)return query(pos,rt<<1|1);
            else if(pos<=mid) return query(pos,rt<<1);
        }
    }
    int n,m;
    vector<int>q[maxn];
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0; i<=m; i++)q[i].clear();
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d",&a[i].l,&a[i].r);
            q[a[i].r-a[i].l+1].push_back(i);
        }
        build(1,m,1);
        for(int i=1; i<=m; i++)
        {
            int ans=0;
            n-=q[i].size();
            for(int j=0; j<q[i].size(); j++) update(a[q[i][j]].l,a[q[i][j]].r,1,1);
            for(int j=i; j<=m; j+=i) ans+=query(j,1);
            printf("%d
    ",ans+n);
        }
    }
    
    
    
    

    树状数组区间更新单点查询
    根据对区间起点和终点的标记,求前缀和即可得到每个点的被覆盖次数
    【以下引用莫老师的树状数组写法 @银月清歌

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<iostream>
    #include<string>
    #include<vector>
    #include<queue>
    #include<map>
    using namespace std;
    typedef long long LL;
    #define M(a,b) memset(a,b,sizeof(a))
    #define pb push_back
    const int maxn = 100000+10;
    const LL mod = 1000000007;
    struct Node
    {
        int l,r;
        bool operator < (const Node &a)const
        {
            return (r-l+1)<(a.r-a.l+1);
        }
    } s[maxn+(maxn<<1)];
    vector<int>g[maxn];
    int c[maxn];
    int n,m;
    void add(int l,int r)
    {
    
        while(l<=m)
        {
            ++c[l];
            l+=l&(-l);
        }
        while(r<=m)
        {
            --c[r];
            r+=r&(-r);
        }
        return ;
    }
    int query(int i)
    {
    
        int res=0;
        while(i)
        {
            res+=c[i];
            i-=i&(-i);
        }
        return res;
    }
    int main()
    {
    
        M(c,0);
        scanf("%d%d",&n,&m);
        for (int i=0; i<=m; ++i)
        {
            g[i].clear();
        }
        for (int i=1; i<=n; ++i)
        {
            scanf("%d%d",&s[i].l,&s[i].r);
            int len=s[i].r-s[i].l+1;
            g[len].pb(i);
        }
        for(int i=1; i<=m; ++i)
        {
            int ans=0;
            for (int j=0; j<g[i].size(); ++j)
            {
                int &pos=g[i][j];
                add(s[pos].l,s[pos].r+1);
            }
            for (int j=i; j<=m; j+=i)
            {
                ans+=query(j);
            }
            n-=g[i].size();
            printf("%d
    ",ans+n);
        }
        return 0;
    }
    
    
    
  • 相关阅读:
    greenlet和gevent模块的区别?
    Python整型int、浮点float常用方法
    jquery的相关应用
    mongoDB在java上面的应用
    SpringAOP的两种实现方式
    svn 服务器搭建 (Linux)
    maven的两种打包方式
    MongoDB 安装与配置
    springmvc拦截器配置
    Linux下安装mysql
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135752.html
Copyright © 2020-2023  润新知