Tunnel Warfare HDU - 1540

原题链接
考察:线段树
完全没思路,乍一眼看还以为是并查集.
大佬的思路:
  D操作:单点修改
  R操作: 单点修改
  Q操作: 区间查询
我:???
思路:
  这道题求最大的连通数的思想有点类似这题Imbalanced Array CodeForces - 817D
  因为村庄都是一维的,所以最大连通数一定是R-L+1(R指由该点向右能扩展的最大右边界).那么每个点的R与L在进行操作的情况下如何求呢?这里就可以把D,R操作看成单点修改,即把修改点的R,L改为0.
  那么查询应该怎么查询?对于每个区间我们只能查到它的L与R.对于查询点,如果它在左子树覆盖的右端点[r-R+1,r]内,那么直接返回左子树R+右子树L.同理右端点.我们只考虑每段区间的中间部分,考虑两端会出现覆盖右(左)子区间的情况.
  还有一个坑点是最后一个毁掉的地方不能用变量存储,因为可能存在连续的R操作.

Code

#include <iostream>
#include <algorithm> 
#include <stack>
using namespace std;
const int N = 50010;
char op[3];
int n,m;
struct Node{
	int l,r,lmax,rmax;
}tr[N<<2];
int get(int u)
{
	return tr[u].r-tr[u].l+1;
}
void push_up(int u)
{
	tr[u].lmax = tr[u<<1].lmax+(tr[u<<1].lmax==get(u<<1)?tr[u<<1|1].lmax:0);
	tr[u].rmax = tr[u<<1|1].rmax+(tr[u<<1|1].rmax==get(u<<1|1)?tr[u<<1].rmax:0); 
}
void build(int u,int l,int r)
{
	tr[u] = {l,r,1,1};
	if(l==r) return;
	int mid = l+r>>1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
	push_up(u);
}
void modify(int u,int idx,int c)
{
	if(tr[u].l==tr[u].r)
	{
		tr[u].lmax = c,tr[u].rmax = c;
		return;
	}
	int mid = tr[u].l+tr[u].r>>1;
	if(idx<=mid) modify(u<<1,idx,c);
	else modify(u<<1|1,idx,c);
	push_up(u);
}
int query(int u,int idx)
{
	if(tr[u].l==tr[u].r) return tr[u].lmax;
	int mid = tr[u].l+tr[u].r>>1;
	if(idx<=mid)
	{
		if(idx>=tr[u<<1].r-tr[u<<1].rmax+1) return tr[u<<1].rmax+tr[u<<1|1].lmax;
		else return query(u<<1,idx);
	}else{
		if(idx<=tr[u<<1|1].l+tr[u<<1|1].lmax-1) return tr[u<<1|1].lmax+tr[u<<1].rmax;
		else return query(u<<1|1,idx);
	}
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		stack<int> stk;
		build(1,1,n);
		int last = 0;
		while(m--)
		{
			scanf("%s",op);
			if(op[0]=='R') modify(1,stk.top(),1),stk.pop();
			else{
				int x;
				scanf("%d",&x);
				if(op[0]=='D') modify(1,x,0),stk.push(x);
				else printf("%d\n",query(1,x));
			} 
		}
	}
	return 0;
}

补充:由此思路延伸的其他做法.

  1. STL.对于每个毁灭村庄,用set存储.对于每个查询x,求set里最大的<x当作L,最小的>x当作R.这就是二分了.
  2. 树状数组. 查找往左最后1个连续1,往右最后一个连续1.
posted @ 2021-05-22 00:06  acmloser  阅读(34)  评论(0编辑  收藏  举报