[BZOJ4003]城池攻占:可并堆+lazy tag

首先,这道题有非常简单的暴力写法,只要对每个人暴力一步步往上走即可。如何优化暴力呢?最容易想到的方法是加速转移过程,即倍增,但空间貌似不太够...那么我们从另一个角度考虑:利用重复的信息。同一段路可能被很多人走过,造成了极大的时间浪费,我们可以考虑把要走同一段路的人集中在一起,这样只要走一次,就能同时判断每个人能否走下去了。怎么操作呢?我们以每一个城池为根建立小根堆,堆中的元素是一个个骑士的战斗力,每次判断时,把所有战斗力不足的骑士弹出,剩下的骑士利用可并堆的合并操作合并到下一个城池。那么问题来了,每次攻占城池后,所有存活骑士的战斗力都会发生变化,逐个修改肯定会超时。我们会发现,修改每个骑士战斗力的操作十分类似线段树的区间修改,因此,我们可以利用lazytag优化时间复杂度。完整代码如下。

#include
#define maxn 300010
#define r register
#define cswap(_x,_y) (_x^=_y,_y^=_x,_x^=_y)
#define add(_x,_y) (s[_x]+=_y,tagb[_x]+=_y)//打加法标记
#define mul(_x,_y) (s[_x]*=_y,tagb[_x]*=_y,tagk[_x]*=_y)//打乘法标记
using namespace std;
int n,m,a,fa[maxn],c[maxn],ansn[maxn],ansm[maxn];
int dep[maxn],root[maxn],dis[maxn],ls[maxn],rs[maxn];
long long h[maxn],k[maxn],b[maxn],s[maxn],tagk[maxn],tagb[maxn];
templatevoid read(r t &x)
{
    r char ch;
    while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
    r bool neg=ch=='-';x=neg?0:ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0';
    if(neg) x=-x;
}
void pushdown(r int x)//切记先乘再加
{
	if(tagk[x]!=1)
	{
		if(ls[x]) mul(ls[x],tagk[x]);
		if(rs[x]) mul(rs[x],tagk[x]);
		tagk[x]=1;
	}
	if(tagb[x])
	{
		if(ls[x]) add(ls[x],tagb[x]);
		if(rs[x]) add(rs[x],tagb[x]);
		tagb[x]=0;
	}
}
int merge(r int x,r int y)
{
	if(!x||!y) return x+y;
	if(s[x]>s[y]) cswap(x,y);
	pushdown(x),rs[x]=merge(rs[x],y);
	if(dis[ls[x]]

 

你可能感兴趣的:([BZOJ4003]城池攻占:可并堆+lazy tag)