zoukankan      html  css  js  c++  java
  • 【线段树】【P3740】 [HAOI2014]贴海报

    传送门

    Description

      Bytetown城市要进行市长竞选,所有的选民可以畅所欲言地对竞选市长的候选人发表言论。为了统一管理,城市委员会为选民准备了一个张贴海报的electoral墙。

    张贴规则如下:

    1. electoral墙是一个长度为N个单位的长方形,每个单位记为一个格子;

    2. 所有张贴的海报的高度必须与electoral墙的高度一致的;

    3. 每张海报以“A B”表示,即从第A个格子到第B个格子张贴海报;

    4. 后贴的海报可以覆盖前面已贴的海报或部分海报。

    现在请你判断,张贴完所有海报后,在electoral墙上还可以看见多少张海报。

    Input

      第一行: N M 分别表示electoral墙的长度和海报个数

      接下来M行: Ai Bi 表示每张海报张贴的位置

    Output

      输出贴完所有海报后,在electoral墙上还可以看见的海报数。

    Sample Input

    100 5
    1 4
    2 6
    8 10
    3 4
    7 10

    Sample Output

    4

    Hint 

      10<= N <= 10000000 1<=M<=1000 1<= Ai <= Bi <=10000000

      所有的数据都是整数。数据之间有一个空格

    Solution

      考虑暴力修改 上界复杂度将达到O(nm)。显然扯淡。

      考虑进行优化。由于是对区间操作。最后输出答案也相当于对区间[1,n]进行询问,于是自然想到对区间操作最拿手的线段树。

      如果在线修改,记录区间上有几个广告,以及区间上广告是否覆盖完全(即最右侧的广告在区间外是否还有一块),合并时f[p]=f[dp]+f[ddp](+1),会产生问题:如样例图片:

              

    观察样例中间被英文标出的地方,使用上述算法,改区间会被判定为有三个广告。但事实上只有两个,因为是上面覆盖了下面的一部分。

      如何解决此问题?考虑广告数量太大无法状压,但不强制在线,于是我们把广告都读进来,倒序插入。因为较早贴上的广告显然无法影响到后面的广告。这里再次使用了阶段划分和无后效性的思想。对于一个区间,如果已经被提前计算的广告完全覆盖,则直接return。一个广告能被看见,当且仅当他能覆盖一个独立的区间。至此,问题得解。

    Code

    #include<cstdio>
    #include<algorithm>
    #define ci const int
    #define maxn 10000010
    #define maxt 40000010
    
    inline void qr(int &x) {
        char ch=getchar(),last=' ';
        while(ch>'9'||ch<'0') last=ch,ch=getchar();
        while(ch>='0'&&ch<='9')    x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        if(last=='-')    x=-x;
    }
    
    inline int max(const int &a,const int &b) {if(a>b) return a;return b;}
    inline int min(const int &a,const int &b) {if(a<b) return a;return b;}
    inline int abs(const int &a) {if(a>=0) return a;return -a;}
    
    inline void swap(int &a,int &b) {int temp=a;a=b;b=temp;}
    
    int n,m,a,b,cnt;
    
    bool tree[maxt],judge;
    
    struct M {
        int l,r;
    };
    M MU[maxn];
    
    void change(ci l,ci r,ci p,ci aiml,ci aimr) {
        if(l>r) return;
        if(l>aimr||r<aiml) return;
        if(l>=aiml&&r<=aimr) {if(!tree[p]) tree[p]=judge=true;return;}
        if(tree[p])    return;
        int mid=(l+r)>>1,dp=p<<1,ddp=dp|1;
        change(l,mid,dp,aiml,aimr);change(mid+1,r,ddp,aiml,aimr);
        tree[p]=tree[dp]&&tree[ddp];
    }
    
    int main() {
        qr(n);qr(m);
        for(int i=1;i<=m;++i) {qr(MU[i].l);qr(MU[i].r);}
        do {
            judge=false;
            change(1,n,1,MU[m].l,MU[m].r);
            if(judge) ++cnt;
        } while(--m);
        printf("%d
    ",cnt);
        return 0;
    }

    Summary

      1、阶段划分是做任何题十分有力的工具,除开DP外,也要时刻应用。
      2、在线难以处理时,考虑离线。正序难以处理时,考虑倒序。无序难以处理时,考虑有序。因为后者往往在一定程度上满足无后效性。

  • 相关阅读:
    【转】理解Ruby的4种闭包:blocks, Procs, lambdas 和 Methods
    折腾weibo开放平台
    netsh——常用命令及使用技巧
    The Enemies of Achievement
    java内存模型
    命名规则
    JS 控制加载页面对象
    点击图片弹出上传文件对话框
    ASP.NET判断用户是否在线
    ASP.Net处理QueryString函数汉字参数传递错误
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9337888.html
Copyright © 2011-2022 走看看