csu1335 高桥与低桥

Description

有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上,所以不算“淹了两次”。举例说明:

假定高桥和低桥的高度分别是5和2,初始水位为1

第一次洪水:水位提高到6(两个桥都被淹),退到2(高桥不再被淹,但低桥仍然被淹)

第二次洪水:水位提高到8(高桥又被淹了),退到3。

没错,文字游戏。关键在于“又”的含义。如果某次洪水退去之后一座桥仍然被淹(即水位不小于桥的高度),那么下次洪水来临水位提高时不能算“又”淹一次。

输入n座桥的高度以及第i次洪水的涨水水位ai和退水水位bi,统计有多少座桥至少被淹了k次。初始水位为1,且每次洪水的涨水水位一定大于上次洪水的退水水位。

Input

输入文件最多包含25组测试数据。每组数据第一行为三个整数n, m, k(1<=n,m,k<=105)。第二行为n个整数hi(2<=hi<=108),即各个桥的高度。以下m行每行包含两个整数ai和bi(1<=bi<ai<=108, ai>bi-1)。输入文件不超过5MB。

Output

对于每组数据,输出至少被淹k次的桥的个数。

Sample Input

2 2 22 56 28 35 3 22 3 4 5 65 34 25 2

Sample Output

Case 1: 1Case 2: 3

Hint

Source

湖南省第九届大学生计算机程序设计竞赛


思路:这道题思路比较清晰,需要对各个桥记录上次退潮时的状况,是否任然处于被淹状态,还要与下次涨潮时的高度对比并记录,这样算起来时间复杂度为O(n^n);绝对会TE,

这里要借助树状数组的知识来解决,灵活的处理数据把复杂度降为(Onlogn),但注意不是照搬板子,难度就在这里。需要自己写出几个函数。算法还是要理解清楚,不能照搬板子,我这次都看不出来要用树状数组。。。。
如果有点难理解为什么sum()函数里面为什么要累加一段区间的c[p],可以去看下树状数组的定义,因为第i座桥控制的用来表达它被淹记录的,不是一个c数组单元,而是一段区间。其他比如说add(),search()也是这个道理    。
具体见代码注释;
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn=1e5+5;
int n,m,k;
int a[maxn],c[maxn],x[maxn],y[maxn];
int lowbit(int k1)
{
    return k1&(-k1);
}
int add(int p,int q)//对某一座桥被淹次数进行操作
{
    while(p>0)
    {
        c[p]+=q;
        p-=lowbit(p);
    }
    return 0;
}
//int count1=1;
int search1(int p)//二分查找到指定位置
{
    int mid,temp=p;
    int rs=n,ls=1;
    while(rs>ls)
    {
        mid=((rs+ls)>>1);
        if(a[mid]>=temp)
        {
            rs=mid;
        }
        else ls=mid+1;
    }
    return rs;
}
int sum(int p)//求出p位置总共被淹了几次
{
    int kk=0;
    while(p<=n)
    {
        kk+=c[p];
        p+=lowbit(p);
    }
    return kk;
}
int main()
{
    int cnt=0;
    while(scanf("%d %d %d",&n,&m,&k)!=EOF)
    {
        memset(c,0,sizeof(c));

        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);

        sort(a+1,a+n+1);
        int p,q,ans,kk;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x[i],&y[i]);
            //cout<<x[i]<<" "<<y[i]<<endl;
            if(i==1)
            {
                p=i;
                q=search1(x[i]);
                //printf("")
                //cout<<123456<<endl;
            }
            else
            {
                p=search1(y[i-1]);//找到上次退潮桥被淹的桥的最高序号,因为拍过序p,所以小与p的都不用加次数,因为都在水里
                q=search1(x[i]);//找到比这次涨潮高度要淹的最高序号q,q与q之下的都要被淹
            }
            add(p,-1);//对小与上次退潮高度的桥被淹次数减1,抵消下面的加1操作
            add(q,1);//对小与这次涨潮高度的桥被淹次数加1
        }
        ans=0;
        for(int i=1;i<=n;i++)
        {

            kk=sum(i);
            //cout<<kk<<" "<<k<<endl;
            if(kk>=k)
            ans++;
        }
        printf("Case %d: %d\n",++cnt,ans);
    }
}









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值