HDU 5741 扫描线+离散化+树状数组

题目大意:

给定若干个01区间,比如有a个连续的0,b个连续的1,c个连续的0,d个连续的1………………

这样的一一大堆数字。

然后问,是否存在这样一个区间[l,r]使得整个区间里0,和1的个数分别为A,B个。


比如00001111100011 这样会被写成4,5,3,2 (只不过为了优化读入时间而已)


然后可以选区间00[0011111000]11 这样,也就是可以切开连续的区间,选择一个区间。这个区间有5个0,5个1.



解法1:

扫描线法。

先离散化所有数字,因为最多有n个连续01串,所以我们可以得到有如下东西:

l,r,L,R  表示,有一个方案,可以得到   0的数量在[l,r]之间,1的数量在[L,R]之间。

其实可以通过穷举i<= j <=n,使得我们知道所有的上述{l,r,L,R}的数值。


对l,r进行扫描线算法(队友和我说这叫扫描线算法)。

在一条直线上,记录事件点.比如有直线[5,10],那么我们记录在5开始的时候,有直线进入,在10结束的时候,有直线退出。

那么,我们就可以扫描这一整条直线,得出在k时刻,当前有多少条(或者有没有)直线覆盖了这个点。


然后再记录一些事件,比如l=5,r=10,L=7,R=15的话

在扫描到5的时候,我们加入(线段树也好,树状数组也好)区间[L,R], 扫描到10的时候,拿走区间[L,R]。


同时,在扫描到5的时候,我们可以查询{A=5,B}的所有询问。 我们把询问按照A排序,然后就可以一边扫描,一边添加/删除线段,一边排序了。


这道题内存不算卡的太严格,但是想偷懒多用内存是不行的…… 


所以所有事件点并不能全部离散,只能离散询问的所有{A,B}


这样,当一条线段[l,r]添加进离散后的区间的话,就有一些细节要想清楚了……比如添加在哪里,删除在哪里,啥时候添加,啥时候删除……


#include <cstdio>
#include <ctime>
#include <vector>
#include <stack>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;

#define pr(x) cout<<#x<<" = "<<x<<" "
#define prln(x)    cout<<#x<<" = "<<x<<endl
const int maxn = 1e6+1000;
const int maxm = 5 * 1e5 + 100;
int w[1111];
int n, m;
//map<int, int>lisan;
struct question    //记录每个询问,题目说有m次
{
	int a, b,id;
}q[maxm];

bool operator < (question A, question B)
{
	return A.a < B.a;
}


struct ls//离散
{
	int lisan[maxn * 2];
	int t;
	void ins(int k)
	{
		++t;
		lisan[t] =k;
	}
	void clear()
	{
		t=0;
	}
	void doit()
	{
		sort(lisan + 1, lisan + 1+ t);

		if (t>=maxn*2)
		{
			int t;
			while(1)
			{
				++t;
				//cout<<"1"<<endl;
			}
		}

		t = unique(lisan + 1, lisan + 1+ t) - (lisan + 1);
	}
	int get(int k)    //查找k的下标
	{
		return lower_bound(lisan + 1, lisan + 1 + t, k) - (lisan);
	}
	int get2(int k)	//查找比K大的下标
	{
		return upper_bound(lisan + 1, lisan + 1 + t, k) - (lisan);
	}

}lisan;


struct qujian
{
	int L,R,id;
}jin[maxn * 2], chu[maxn*2];
int qujianT;
int st[maxn * 2], ed[maxn * 2];

bool operator < (qujian A, qujian B)
{
	return A.id < B.id;
}

void make(int l, int r, int L, int R)
{
	//cout<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
	int a = lisan.get(l);
	int b = lisan.get2(r);
	
	//cout<<"#"<<b<<endl;
	if (r < lisan.lisan[b])
	{
		--b;
	}
	if (a>b)	return;

	++qujianT;
//	cout<<"# "<<a<<" ->"<<b<<" "<<r<<" "<<lisan.lisan[b]<<endl;
//	if (r > lisan.lisan[b])	--b;
//	cout<<a<<" ->"<<b<<endl;
	++st[a];
	++ed[b];
	jin[qujianT]={L,R,a};
	chu[qujianT]={L,R,b};
}

void init()
{
	lisan.clear();
	scanf("%d%d", &n, &m);
	for (int i = 1; i <=n;++i)    
	{
		scanf("%d", &w[i]);
	}
	for (int i = 1; i <= m; ++ i)
	{
		scanf("%d%d",&q[i].a, &q[i].b);
		q[i].id=i;
		lisan.ins(q[i].a);
		lisan.ins(q[i].b);
	}
	lisan.ins(1e9+9);
	lisan.ins(-1);
	lisan.doit();	//离散化进行排序
	memset(st,0,sizeof(st));
	memset(ed,0,sizeof(ed));
	qujianT=0;
	for (int i = 1; i <= n; ++ i)
	{
		//奇数都是0,偶数都是1的
		int l=0,r=0, L=0,R=0;
		if (i&1)    r= w[i];
		else R = w[i];
		make(l,r,L,R);
		if (i==n)    continue;
		if (i&1)
		{
			R = w[i + 1];
		}else r = w[i + 1];
		make(l,r,L,R);
	}
	for (int i = 1; i <=n - 2 ; ++ i)
	{
		int l=0,r=0,L=0,R=0;
		if (i&1)    r=w[i], R=w[i + 1];
		else        R=w[i],    r=w[i + 1];
		for (int j = i + 2; j <=n; ++ j)
		{
			if (j&1)    
			{
				L += w[j - 1],    r += w[j];
			}
			else l += w[j - 1], R += w[j];
			//qj[++qt]={l,r,L,R};
			make(l,r,L,R);
		}
	}
	sort(jin + 1, jin + 1 + qujianT);//进出的L,R区间,按照出现的位置排序
	sort(chu + 1, chu + 1 + qujianT);
}

int s[maxn];

inline void ins(int k,int num)
{
	for (;k<=lisan.t + 5;k += (k&-k))
	{
		s[k]+=num;
	}
}

inline int ask(int k)
{
	++k;
	int ret=0;
	for (;k>=1; k -= k&-k)    ret+=s[k];
	return ret;
}

inline void insert(int l, int r)
{
	++l;
	++r;
	ins(l,1);
	ins(r,-1);
}

inline void del(int l, int r)
{
	++l;
	++r;
	ins(l, -1);
	ins(r, 1);
}

int output[maxn];

void doit()
{
	memset(s,0,sizeof(s));
	sort(q + 1, q + 1 + m);    

	int wenti=1, now=0;//第几个问题了,现在有几个a
	int jinT=1,chuT=1;

	for (int i = 1; i<= lisan.t; ++ i)
	{
		now += st[i];
		while (jinT <= qujianT && jin[jinT].id ==i)
		{
			int l , r;
			l = lisan.get(jin[jinT].L);
			r = lisan.get(jin[jinT].R + 1);
			insert(l ,r);
			++jinT;
		}
		while (wenti <= m && lisan.get(q[wenti].a)==i)    
		{
			output[q[wenti].id] = now && ask(lisan.get(q[wenti].b)) ;
			++wenti;
		}
		now -= ed[i];
		while (chuT <= qujianT && chu[chuT].id == i)
		{
			int l , r ;
			l = lisan.get(chu[chuT].L);
			r = lisan.get(chu[chuT].R + 1);
			del(l ,r);
			++chuT;

		}
	}
	for (int i = 1; i <= m; ++ i)
		printf("%d", output[i]>=1);
	printf("\n");
}

int main()
{
	int T;
	double st=clock();
	scanf("%d", &T);
	while (T--)
	{
		init();
		doit();
	}
	//cout<<"程序运行时间为:  "<< (clock()-st)/1000<<endl;
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值