Problem 1004: 蛤玮打扫教室
Time Limits: 1000 MS Memory Limits: 65536 KB
64-bit interger IO format: %lld Java class name: Main
Description
现在知道一共有n个机房,算上蛤玮一共有m个队员,教练做了m个签,每个签上写着两个数L,R(L<=R),抽到的人要把[L,R]的教室全部打扫一遍.由于蛤玮是队长而且他很懒,他通过某种交易提前知道了所有m个签上面写的是什么,而且通过某种魔法可以控制自己抽到哪个签.一个教室被打扫一次就干净了,所以蛤玮想知道自己抽哪些签可以不用打扫教室而且不会被教练发现,即他抽到的区间全都会被别人打扫一遍.
蛤玮被教练叫去打扫机房,集训队有很多机房,也有很多队员,现在他们要用抽签的方式决定谁打扫哪间教室.
Input
第一行为一个整数T(1<=T<=20),代表数据组数。每组数据第一行n,m(1<=n,m<=100000),接下来m行,每行两个数L,R(1<=L<=R<=n).
Output
每组数据输出一个k,表示多少个签符合蛤玮的要求,接下来一行输出k个数,这些签的编号,下标从1开始.
Sample Input
3 15 5 1 4 5 5 6 8 9 10 5 6 3 6 1 1 1 1 2 2 2 2 3 3 3 3 10 3 1 4 2 6 6 10
Output for Sample Input
2 2 5 6 1 2 3 4 5 6 0
Hint
Author
思路:在vis数组里对每一个区间的L与R操作,vis[L]++,vis[R+1]--,这样可以标记一个区间的端点,然后再用这个数组求一个前缀和放入per,每一项即是对应点的被覆盖次数,然后这样就稍微好办了点,转化为告诉你每一个点的覆盖次数,求是否题目中的抽签的区间是否全部都是至少被覆盖两次的点,然后再开一个cnt数组,记录连续完整覆盖的区间长度。最后比较每一个抽签R-L+1(区间长度)与cnt[R]-cnt[L-1](连续覆盖区间长度)是否相等即可。不愧是大牛想出来的办法。
代码:
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define MM(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long LL;
const int N=100010;
struct info
{
int l;
int r;
};
info point[N];//抽签信息记录
int vis[N];//端点标记
int cnt[N];//连续覆盖次数大于2区间
int ans[N];//记录答案
int per[N];//单点覆盖次数
int main(void)
{
int tcase,i,j,L,R,x,y,z,n,m;
scanf("%d",&tcase);
while (tcase--)
{
MM(point);MM(vis);MM(cnt);MM(ans);MM(per);
scanf("%d%d",&n,&m);
for (i=1; i<=m; i++)
{
scanf("%d%d",&point[i].l,&point[i].r);
vis[point[i].l]++;
vis[point[i].r+1]--;
}
int temp=0;
for (i=1; i<=n; i++)
{
temp+=vis[i];
per[i]=temp;
}
for (i=1; i<=n; i++)
{
if(per[i]>=2)
cnt[i]=cnt[i-1]+1;
else
cnt[i]=0;//或cnt[i]=cnt[i-1]也可以,反正最后求的是差值
}
int k=0;
for (i=1; i<=m; i++)
{
int left=point[i].l;
int righ=point[i].r;
if(righ-left+1==cnt[righ]-cnt[left-1])
ans[k++]=i;
}
printf("%d\n",k);
for (i=0; i<k; i++)
printf("%d%s",ans[i],i==k-1?"\n":" ");
}
return 0;
}