[ZOJ 3324][BNUOJ 16842] Machine [线段树]

有一个神奇的机器,它由一排按键构成,在这种机器上有两种操作,第一种是同时按下[l,r]的所有键,第二种是释放[l,r]的所有键。所有的释放操作均对应一个同区间的按下操作,并且释放操作在按下操作后边进行。若一个按键被按下两次释放一次,则其还是被按下的。现在给定若干次操作,问每次操作后没有被按下的键的段数。如果一段区间[l,r]里的所有的键都没有被按下,那么他们算作一段。

数据范围:总长度不超过10^8,操作次数不超过20000

先对数据离散化,因为只有2W次操作,操作又是成对出现的,所以最多出现2W个不同的点,假使每两个点之间都还有其他的点,这些点永远是相同状态的所以可以缩成一个点。这样最多有4W个叶子节点。

线段树,用类似矩形面积并里边的线段树的那种算法,每个节点记录自己被按下了多少次,然后记录自己的状态,包含左边是否被按下,右边是否被按下,中间有多少个被按下的段。如果该段是整体被按下,则中间被按下的段记录为-1。没有懒操作。仅在某个节点的所有祖先节点都没有被按下的情况下,这个节点的信息才是有效的,否则这个结点一定是全被按下的。

#include <cstdio>
#include <cstring>
#include <map>
#include <set>

using namespace std;

struct State{
	bool l,r;
	int n;
	State() {
		n=-1;
		l=r=true;
	}
	State(bool ll,bool rr,int nn) {
		l=ll;r=rr;n=nn;
	}
};
struct Opt {
	int l,r;
	bool p;
	void read() {
		char c;
		scanf(" %c",&c);
		p=(c=='p');
		scanf("%d%d",&l,&r);
	}
};
struct Node {
	int l,r,times;
	State s;
	Node *ls,*rs;
	Node() {}
	Node(int ll,int rr) {
		l=ll;r=rr;
		ls=rs=NULL;
		s=State();
		times=0;
	}
	void update() {
		if (times!=0) {
			s=State(false,false,0);
			return;
		}
		s=State(true,true,-1);
		if (l==r) return;
		State sl=ls->s,sr=rs->s;
		s.l=sl.l;
		s.r=sr.r;
		s.n=sl.n+sr.n+sl.r+sr.l-(sl.r&&sr.l);
	}
	void *operator new(size_t,void *p) {
		return p;
	}
};
struct SegTree {
	Node *root,*cur;
	Node a[100000];
	Node *maketree(int l,int r) {
		Node *ans=new(cur++)Node(l,r);
		if (l==r) return ans;
		int t=(l+r)/2;
		ans->ls=maketree(l,t);
		ans->rs=maketree(t+1,r);
		return ans;
	}
	void clear(int n) {
		cur=a;
		root=maketree(0,n-1);
	}
	void print() {
		for (Node *i=a;i!=cur;i++) {
			//printf("Node %d: l=%d, r=%d, ls=%d, rs=%d\n",i-a,i->l,i->r,i->ls-a,i->rs-a);
			//printf("   press=%d state=%d,%d,%d\n",i->times,i->s.l,i->s.r,i->s.n);
		}
	}
	void press(Node *from,int l,int r) {
		if (from->l==l&&from->r==r) {
			from->times++;
			from->update();
			return;
		}
		int t=(from->l+from->r)/2;
		if (t<l) press(from->rs,l,r);
		else if (t>=r) press(from->ls,l,r);
		else {
			press(from->ls,l,t);
			press(from->rs,t+1,r);
		}
		from->update();
	}
	void release(Node *from,int l,int r) {
		if (from->l==l&&from->r==r) {
			from->times--;
			from->update();
			return;
		}
		int t=(from->l+from->r)/2;
		if (t<l) release(from->rs,l,r);
		else if (t>=r) release(from->ls,l,r);
		else {
			release(from->ls,l,t);
			release(from->rs,t+1,r);
		}
		from->update();
	}
};

set <int> c;
map <int,int> d;
int n,m,p;
Opt b[20000];
SegTree a;

int main() {
	int t,tt,last,i;
	scanf("%d",&t);
	for (tt=1;tt<=t;tt++) {
		printf("Case #%d:\n",tt);
		scanf("%d%d",&n,&m);
		c.clear();
		d.clear();
		for (i=0;i<m;i++) {
			b[i].read();
			c.insert(b[i].l);
			c.insert(b[i].r);
		}
		p=0;
		last=-1;
		for (set<int>::iterator it=c.begin();it!=c.end();it++) {
			if (*it-1!=last) p++;
			//printf("tran: %d %d\n",*it,p);
			d[last=*it]=p++;
		}
		if (last!=n-1) p++;
		a.clear(p);
		for (i=0;i<m;i++) {
			if (b[i].p) a.press(a.root,d[b[i].l],d[b[i].r]);
			else a.release(a.root,d[b[i].l],d[b[i].r]);
			State tmp=a.root->s;
			printf("%d\n",tmp.n+tmp.l+tmp.r);
			//a.print();
		}
	}
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值