【BZOJ1984】月下“毛景树”-树链剖分

题目大意:有一棵树有N个点,每条边有边权,有4种操作:1.Change i w:将输入的第i条边的权值修改为w。2.Cover u v w:将点u和点v之间路径上的边权都修改为w。3.Add u v w:将点u和点v之间路径上的边权都加上w。4.Max u v:询问点u和点v之间路径上边权的最大值。对于每个询问,给出正确的答案。

做法:由于有多种操作,而且有两个区间修改的操作,所以需要两个lazytag:add(Add操作的标记,表示当前区间的数全部加上了add这个值)和cov(Cover操作的标记,表示当前区间的数都为cov这个值)。需要注意的是,这两个lazytag不能同时起效,否则会导致奇怪的错误......在Add操作中,如果当前区间被操作区间完全包含,且当前区间的cov标记不为-1(即有效),则直接将值加进cov中,否则将值加进add中。在Cover操作中,如果当前区间被操作区间完全包含,则把当前区间的add标记归零,将cov改成要修改成的值。具体的步骤见代码。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#define inf 999999999
using namespace std;
int t,n,tot,f[100010],siz[100010],dep[100010],pos[100010],p,a,b,c;
int d[100010][3],first[100010],son[100010],top[100010],root;
int qpos[100010];
char op[10];
struct edge
{
  int v,next;
}e[20010];
int seg[400010],segp[400010],cov[400010]; //segp为add标记,cov为cov标记,seg记录区间最大值

void readin(int a,int b,int c)
{
  e[++tot].v=b;
  e[tot].next=first[a];
  first[a]=tot;
}

void dfs1(int now)
{
  siz[now]=1;son[now]=0;
  for(int i=first[now];i>0;i=e[i].next)
    if (e[i].v!=f[now])
	{
	  dep[e[i].v]=dep[now]+1;
	  f[e[i].v]=now;
	  dfs1(e[i].v);
	  siz[now]+=siz[e[i].v];
	  if (siz[e[i].v]>siz[son[now]]) son[now]=e[i].v;
	}
}

void dfs2(int now,int chain)
{
  pos[now]=++p;top[now]=chain;
  if (son[now]) dfs2(son[now],chain);
  for(int i=first[now];i>0;i=e[i].next)
    if (e[i].v!=f[now]&&e[i].v!=son[now])
	  dfs2(e[i].v,e[i].v);
}

void pushdown(int no)
{
  if (cov[no]!=-1)
  {
	cov[no<<1]=cov[(no<<1)+1]=cov[no];
	seg[no<<1]=seg[(no<<1)+1]=cov[no];
	segp[no<<1]=segp[(no<<1)+1]=0;
	cov[no]=-1;
  }
  if (segp[no]!=0)
  {
    seg[no<<1]+=segp[no];seg[(no<<1)+1]+=segp[no];
    if (cov[no<<1]==-1) segp[no<<1]+=segp[no];
	else cov[no<<1]+=segp[no];
	if (cov[(no<<1)+1]==-1) segp[(no<<1)+1]+=segp[no];
	else cov[(no<<1)+1]+=segp[no];
	segp[no]=0;
  }
}

void pushup(int no)
{
  seg[no]=max(seg[no<<1],seg[(no<<1)+1]);
}

void buildtree(int no,int l,int r)
{
  int mid=(l+r)>>1;
  segp[no]=0;cov[no]=-1;
  if (l==r) {seg[no]=d[qpos[l]][2];return;}
  buildtree(no<<1,l,mid);
  buildtree((no<<1)+1,mid+1,r);
  pushup(no);
}

void change(int no,int l,int r,int a,int c)
{
  int mid=(l+r)>>1;
  if (l==r) {seg[no]=c;return;}
  pushdown(no);
  if (a<=mid) change(no<<1,l,mid,a,c);
  else change((no<<1)+1,mid+1,r,a,c);
  pushup(no);
}

void segadd(int no,int l,int r,int s,int t,int w)
{
  int mid=(l+r)>>1;
  if (l>=s&&r<=t)
  {
    if (cov[no]==-1) segp[no]+=w;
	else {cov[no]+=w;segp[no]=0;}
	seg[no]+=w;
	return;
  }
  pushdown(no);
  if (s<=mid) segadd(no<<1,l,mid,s,t,w);
  if (t>mid) segadd((no<<1)+1,mid+1,r,s,t,w);
  pushup(no);
}

void segcov(int no,int l,int r,int s,int t,int w)
{
  int mid=(l+r)>>1;
  if (l>=s&&r<=t)
  {
    if (segp[no]) segp[no]=0;
	seg[no]=w;
	cov[no]=w;
	return;
  }
  pushdown(no);
  if (s<=mid) segcov(no<<1,l,mid,s,t,w);
  if (t>mid) segcov((no<<1)+1,mid+1,r,s,t,w);
  pushup(no);
}

int querymx(int no,int l,int r,int s,int t)
{
  int mid=(l+r)>>1;
  if (l>=s&&r<=t) return seg[no];
  int mx=-inf;
  pushdown(no);
  if (s<=mid) mx=max(mx,querymx(no<<1,l,mid,s,t));
  if (t>mid) mx=max(mx,querymx((no<<1)+1,mid+1,r,s,t));
  pushup(no);
  return mx;
}

int query(int a,int b)
{
  int mx=-inf;
  while(top[a]!=top[b])
  {
    if (dep[top[a]]<dep[top[b]]) swap(a,b);
	mx=max(mx,querymx(1,1,p,pos[top[a]],pos[a]));
	a=f[top[a]];
  }
  if (dep[a]<dep[b]) swap(a,b);
  if (a!=b) mx=max(mx,querymx(1,1,p,pos[son[b]],pos[a]));
  return mx;
}

void add(int a,int b,int w)
{
  while(top[a]!=top[b])
  {
    if (dep[top[a]]<dep[top[b]]) swap(a,b);
	segadd(1,1,p,pos[top[a]],pos[a],w);
	a=f[top[a]];
  }
  if (dep[a]<dep[b]) swap(a,b);
  if (a!=b) segadd(1,1,p,pos[son[b]],pos[a],w);
}

void cover(int a,int b,int w)
{
  while(top[a]!=top[b])
  {
    if (dep[top[a]]<dep[top[b]]) swap(a,b);
	segcov(1,1,p,pos[top[a]],pos[a],w);
	a=f[top[a]];
  }
  if (dep[a]<dep[b]) swap(a,b);
  if (a!=b) segcov(1,1,p,pos[son[b]],pos[a],w);
}

void read()
{
  op[0]=' ';
  while (op[0]<'A'||op[0]>'Z') scanf("%s",op);
}

void input()
{
  scanf("%d",&n);
  root=1;
  memset(first,0,sizeof(first));
  memset(seg,0,sizeof(seg));
  tot=f[root]=dep[root]=p=0;
  for(int i=1;i<=n-1;i++)
  {
    scanf("%d %d %d",&a,&b,&c);
	d[i][0]=a;d[i][1]=b;d[i][2]=c;
	readin(a,b,c);readin(b,a,c);
  }
  dfs1(root);
  dfs2(root,root);
  for(int i=1;i<n;i++)
  {
	if (dep[d[i][0]]>dep[d[i][1]]) swap(d[i][0],d[i][1]);
	qpos[pos[d[i][1]]]=i;
  }
  buildtree(1,1,p);
}

void work()
{
  int a,b;
  for(read();op[0]!='S';read())
  {
    int w;
    scanf("%d %d",&a,&b);
    if (op[0]=='M') printf("%d\n",query(a,b));
	else if (op[0]=='A') {scanf("%d",&w);add(a,b,w);}
         else
		 {
		   if (op[1]=='h') change(1,1,p,pos[d[a][1]],b);
		   else {scanf("%d",&w);cover(a,b,w);}
		 }
  }
}

int main()
{
  input();
  work();
  
  return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值