方法一:用链表存区间,每次上色的操作,依次取出所有的区间,用上色的区间去覆盖,返回新的区间。
由于每次都要遍历一遍所有的区间,比方法二慢。
方法二:离散化的线段树
先把所有区间的端点列出来,排序。
然后用线段树来做,最后将连续的白色区间合并,再计算答案。
代码写的比较乱。
方法一代码:
#define FOR(i,n) for(long long (i)=1;(i)<=(n);(i)++)
#define For(i,n) for(long long (i)=0;(i)<(n);(i)++)
using namespace std;
struct Node{
int L,R;//横坐标
bool Flag;//这段是黑1,白0
Node *next;
};
Node *List; //区间链表的头指针
int N;
Node *NewList=NULL,*NewEnd=NULL;
void Insert(int a,int b,bool v); //区间上色
void Add(Node *T);//将T加入新链表
Node *AddIn(int a,int b,bool v);//辅助函数,生成一个 区间的结构体。
int main(void)
{
while(cin>>N){
Node *temp=new Node;List=NULL;
temp->L=0;temp->R=1000000000;temp->Flag=0;
temp->next=List;List=temp;
For(i,N) {
int a,b;char c;
cin>>a>>b>>c;
Insert(a,b,c=='b');
}
int Max=-1;int L=-1,R=-1;
//搜索所有区间,找最长的区间。
temp=List;
while(temp){
if(!temp->Flag) if(temp->R-temp->L>Max) Max=temp->R-temp->L,L=temp->L,R=temp->R;
temp=temp->next;
}
printf("%d %d\n",L,R);
}
return 0;
}
void Insert(int a,int b,bool v){
NewList=NULL;NewEnd=NULL;
int T=0;//是否已经完成操作。
while(List){
//取出一个元素
Node *temp=List;
List=List->next;
if(T || (temp->R < a) ) {
Add(temp);
continue;
}
//比较是否需要更改,将结果加入新链表
if(b <=temp->R){
if(temp->Flag!=v){//分割成3区间
Add(AddIn(temp->L,a,temp->Flag));
Add(AddIn(a,b,v));
Add(AddIn(b,temp->R,temp->Flag));
T=1;continue;
}
else{
Add(temp);//不操作,直接放回
T=1;
continue;
}
}
else{
if(temp->Flag!=v){
Add(AddIn(temp->L,a,temp->Flag));
Add(AddIn(a,temp->R,v));
a=temp->R;
}
else{
a=temp->R;
Add(temp);
}
}
}
List=NewList;
}
void Add(Node *T){
if(T==NULL) return;
T->next=NULL;
if(NewEnd){
if(NewEnd->R==T->L){
if(NewEnd->Flag==T->Flag){
NewEnd->R=T->R;
delete T;
return;
}
}
NewEnd->next=T;
NewEnd=T;
NewEnd->next=NULL;
}
else{
NewList=T;
NewEnd=T;
NewEnd->next=NULL;
}
}
Node *AddIn(int a,int b,bool v){
if(a>=b) return NULL;
Node *temp=new Node;
temp->L=a,temp->R=b,temp->Flag=v;temp->next=NULL;
return temp;
}
方法二代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <map>
#define inf 0x5fffffff
#define PN 10010 //点数
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
struct P{
int x;
bool operator <(const P&B)const{return x<B.x;}
}p[PN];int Pn;
struct Seg{
int l,r;
bool v;
void set(int l,int r){
this->l=l;this->r=r;
}
}s[PN];
struct Node{//点
int v;//黑1 白0 混色-1
}node[PN <<2];
int N;
void PushDown(int rt){
if(~node[rt].v){//如果是纯色
node[rt<<1].v=node[rt].v;
node[rt<<1|1].v=node[rt].v;
node[rt].v=-1;
}
}
void build(int l,int r,int rt){
if(l==Pn) return;
if(l==r){
node[rt].v=0;
return;
}
int m=(l+r)>>1;
build(ls);
build(rs);
node[rt].v=0;
}
void insert(int L,int R,bool v,int l,int r,int rt){
if(l==Pn) return;
if(L <=p[l].x && p[r].x <R){
node[rt].v=v;
return;
}
if(l==r&&p[r].x==R) return;
PushDown(rt);
int m=(l+r)>>1;
if(L <=p[m].x) insert(L,R,v,ls);
if(R > p[m].x) insert(L,R,v,rs);
}
int Sn;
void FullSearch(int l,int r,int rt){
if(l==Pn) return;
if(l==r){
if(!node[rt].v){
s[Sn++].set(p[l].x,p[l+1].x);
}
return;
}
PushDown(rt);
int m=(l+r)>>1;
FullSearch(ls);
FullSearch(rs);
}
int main(void)
{
while(cin>>N){
//建立所有的端点
Pn=3;
map<int,int> MP;
p[1].x=0;
p[2].x=1000000000;
FOR(i,N) {
int a,b;char c;
scanf("%d%d %c",&s[i].l,&s[i].r,&c);
s[i].v=c=='b';
int I;
//用map来判重,去掉重复的端点。
if(MP.count(s[i].l)) I=MP[s[i].l];
else MP[s[i].l]=Pn++,I=MP[s[i].l],p[I].x=s[i].l;
if(MP.count(s[i].r)) I=MP[s[i].r];
else MP[s[i].r]=Pn++,I=MP[s[i].r],p[I].x=s[i].r;
}
MP.clear();Pn--;
//排序
sort(p+1,p+Pn+1);
//建立线段树,初始化为白色
build(1,Pn,1);
//进行区间上色
FOR(i,N) insert(s[i].l,s[i].r,s[i].v,1,Pn,1);//,OUT1(i),FullSearch(1,Pn,1);//OUT3(s[i].l,s[i].r,s[i].v);
Sn=1;
//搜索所有区间,将白色的存入 s数组
FullSearch(1,Pn,1);
int Cur=1;
//合并相邻的白色区间
for(int i=2;i<Sn;i++){
if(s[Cur].r==s[i].l){
s[Cur].r=s[i].r;
}
else{
Cur++;
s[Cur]=s[i];
}
}
//搜索最长的白色区间。
Seg ANS=s[1];
for(int i=2;i<=Cur;i++){
if(s[i].r-s[i].l>ANS.r-ANS.l){
ANS=s[i];
}
}
//输出答案
printf("%d %d\n",ANS.l,ANS.r);
}
return 0;
}