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);
}
}