题意:
s个士兵排成一列,每次有L到R这些原本健在的士兵死亡,求L-1到1第一个还活着的士兵的编号,以及R+1到s第一个活着的士兵的编号,不存在则输出*。
题解:
方法一:
用并查集,将两种查询分开,如第一个并查集,L到R合并在一起,根为L,表示L+1到R这些人都死了,且R+1和L还活着,那么L到1第一个还活着的士兵自然是L。由于每个士兵只修改一次,所以是O(N)的。
方法二:
用线段树,每个节点记录一个l和r,分别表示最右边第一个活着的士兵的编号和最左边第一个活着的士兵的编号。若L到R的人死了,那么分别查询1到L-1最大的l和R+1到s最小的r。然后将查询的值输出后覆盖到L到R这个区间内。
//Time:172ms
//Length:1095B
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
const int MAXN = 100010;
using namespace std;
int fal[MAXN],far[MAXN];
int findfa(int n,int arr[])
{
if(arr[n]!=n) arr[n]=findfa(arr[n],arr);
return arr[n];
}
void sub(int a,int b,int arr[])
{
a=findfa(a,arr),b=findfa(b,arr);
arr[b]=a;
}
int cal2(int l,int r)
{
for(int i=l;i<=r;)
sub(i+1,i,far),i=findfa(i,far);
return findfa(l,far);
}
int cal1(int r,int l)
{
for(int i=r;i>=l;)
sub(i-1,i,fal),i=findfa(i,fal);
return findfa(l,fal);
}
int main()
{
//freopen("/home/moor/Code/input","r",stdin);
int s,b;
while(scanf("%d%d",&s,&b)&&s&&b)
{
for(int i=0;i<=s+1;++i) fal[i]=far[i]=i;
int l,r;
for(int i=0;i<b;++i)
{
scanf("%d%d",&l,&r);
int tmp=cal1(r,l);
if(tmp==0) printf("* ");
else printf("%d ",tmp);
tmp=cal2(l,r);
if(tmp==s+1) printf("*\n");
else printf("%d\n",tmp);
}
printf("-\n");
}
return 0;
}