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;
}
补充:由此思路延伸的其他做法.
- STL.对于每个毁灭村庄,用set存储.对于每个查询x,求set里最大的<x当作L,最小的>x当作R.这就是二分了.
- 树状数组. 查找往左最后1个连续1,往右最后一个连续1.