2018级第一次上机《点灯》题解

题目描述

点灯

  时间限制: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值