题目描述
点灯
时间限制:300ms 内存限制:65536kb
有n个灯,编号0∼n−10∼n−1,一开始都是关闭状态。
每次操作会拨动一个区间[L,R]灯的开关,也就是说,对于灯i,L≤i≤RL≤i≤R,如果i是关闭状态,则操作会使灯亮,反之会使灯灭。
请问k次操作后有多少灯亮着。
输入
多组输入数据
每组数据第一行两个数n,k(1≤n≤109,1≤k≤1051≤n≤109,1≤k≤105)
接下来k行,每行两个数l,r(0≤l≤r≤n−10≤l≤r≤n−1)
输出
每组数据一行一个数,表示最后灯亮的个数
输入样例
10 1
2 6
输出样例
5
题解
首先,拿到这个题,一个很直观的想法就是将n个灯放在一个数组里面并将其置0,然后根据输入的区间将对应的数组元素+1,最后用一次遍历,数组元素若为偶数,则该灯关闭,数组元素为奇数,则灯打开。
但对于这个题,这个思路显然是不行的。
1.本题最大可能渠道10^9个灯,若用数组储存,内存会爆掉。
2 本题最大限制时间只有300ms 若以灯为分析对象,即使时间复杂度为O(n),在数量达到10^9的数据下,也会TLE。
因此,我们只能以k个输入的区间为研究对象。
解题思路如下
我们定义一个结构体数组,来储存所给区间的左端点的位置以及右端点的位置+1(假设此时左端点为x,右端点为y,此时被改变状态的灯是x-y,被改变状态的灯的数量是y+1-x),并用一个标签flag判断其属性(flag=0代表为左端点,flag=1代表其为右端点)结构体如下:
typedef struct lpoint{
int x;//储存端点的位置
int flag;//储存端点的属性
}light_point;
对于每一次区间的输入,我们都将其保存在该结构体数组之中,由于每个区间有两个端点,因此结构体数组应当有2*k个元素。
最后,我们用快速排序将该数组按从小到大的顺序进行排序。
显然,经过多次开关灯操作之后我们亮灯的区间被分为了很多个小区间,而对于这些小区间,我们可以用上述数组进行查找,步骤如下:
首先我们设置一个临时变量tmp,用于表示当前灯的状态。
然后我们对该结构体数组进行遍历,对于当前所访问的店,如果它是l(即flag=0),将tmp+1,如果它是r,将tmp-1;
显然,当tmp为偶数的时候,代表了两次开关操作,此时开着的灯就是结构体当前数组元素(a[i].x)-上一个数组元素(a[i-1].x),最后,将所有区间开着的灯的数量加和,就得到了答案;
AC代码如下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct lpoint{
int x;
int flag;
}light_point;
light_point a[200005];
int cmp(const void *p1,const void *p2){
light_point *q1,*q2;
q1=(light_point *)p1;
q2=(light_point *)p2;
return q1->x-q2->x;
}
int main()
{
int k,i,j;
int sum;
int tmp;
int n,x,y;
while(scanf("%d%d",&n,&k)!=EOF){//多次数据输入
sum=0;
tmp=0;
memset(a,0,sizeof(a));//更新变量,以防受上次输入的影响
for(i=0;i<2*k;i++){
scanf("%d",&a[i].x);//输入左端点
a[i].flag=0;
i++;
scanf("%d",&a[i].x);//输入右端点
a[i].x=a[i].x+1;
a[i].flag=1;
}
qsort(a,2*k,sizeof(light_point),cmp);
for(i=0;i<2*k;i++){
if(a[i].flag == 0){//a[i]为左端点
tmp++;
if(tmp%2 == 0){
sum+=a[i].x-a[i-1].x;
}
}
else{//a[i]为右端点
tmp--;
if(tmp%2==0){
sum+=a[i].x-a[i-1].x;
}
}
}
printf("%d\n",sum);
}
return 0;
}