BZOJ 3052 [wc2013]糖果公园

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=3052

思路

达成成就:卡BZOJ*1(逃)
这道题……大概就是一道树上莫队吧。
A点B点跳的方法很玄学,具体见代码。

代码

#include <cstdio>
#include <cmath>
#include <algorithm>

const int maxn=100000;

struct query
{
  int l,r,t,id;
};

struct modify
{
  int pos,prec,nowc;
};

query q[maxn+10];
modify d[maxn+10];
int n,m,qq,belong[maxn+10],val[maxn+10],cur[maxn+10],color[maxn+10],block_cnt,dfn_cnt;
int pre[maxn*2+10],now[maxn+10],son[maxn*2+10],tot,fa[maxn+10][20],size,cnt[maxn+10];
int nowl,nowr,nowt,deep[maxn+10],block_size[maxn+10],dfn[maxn+10],vis[maxn+10],numq,numc;
long long nowans,ans[maxn+10];

bool cmp(const query &a,const query &b)
{
  if(belong[a.l]==belong[b.l])
    {
      if(belong[a.r]==belong[b.r])
        {
          return a.t<b.t;
        }
      return belong[a.r]<belong[b.r];
    }
  return belong[a.l]<belong[b.l];
}

inline int ins(int a,int b)
{
  pre[++tot]=now[a];
  now[a]=tot;
  son[tot]=b;
  return 0;
}

int dfs(int u)
{
  if((!fa[u][0])||(block_size[belong[fa[u][0]]]>=size))
    {
      belong[u]=++block_cnt;
      block_size[block_cnt]=1;
    }
  else
    {
      belong[u]=belong[fa[u][0]];
      ++block_size[belong[u]];
    }
  deep[u]=deep[fa[u][0]]+1;
  dfn[u]=++dfn_cnt;
  int j=now[u];
  while(j)
    {
      int v=son[j];
      if(v!=fa[u][0])
        {
          fa[v][0]=u;
          dfs(v);
        }
      j=pre[j];
    }
  return 0;
}

int search()
{
  size=(int)pow(n,2.0/3.0);
  dfs(1);
  for(register int j=1; j<=18; ++j)
    {
      for(register int i=1; i<=n; ++i)
        {
          fa[i][j]=fa[fa[i][j-1]][j-1];
        }
    }
  return 0;
}

inline int lca(int a,int b)
{
  if(deep[a]<deep[b])
    {
      std::swap(a,b);
    }
  for(register int i=18; i>=0; --i)
    {
      if(deep[fa[a][i]]>=deep[b])
        {
          a=fa[a][i];
        }
    }
  if(a==b)
    {
      return a;
    }
  for(register int i=18; i>=0; --i)
    {
      if(fa[a][i]!=fa[b][i])
        {
          a=fa[a][i];
          b=fa[b][i];
        }
    }
  return fa[a][0];
}

int change(int a)
{
  vis[a]^=1;
  if(vis[a])
    {
      nowans+=1ll*cur[++cnt[color[a]]]*val[color[a]];
    }
  else
    {
      nowans-=1ll*cur[cnt[color[a]]--]*val[color[a]];
    }
  return 0;
}

inline int change(int pos,int col)
{
  if(vis[pos])
    {
      nowans-=1ll*cur[cnt[color[pos]]--]*val[color[pos]];
      nowans+=1ll*cur[++cnt[col]]*val[col];
    }
  color[pos]=col;
  return 0;
}

inline int reverse(int a,int b)
{
  while(a!=b)
    {
      if(deep[a]<deep[b])
        {
          std::swap(a,b);
        }
      change(a);
      a=fa[a][0];
    }
  return 0;
}

inline int solve(int x)
{
  while(nowt<q[x].t)
    {
      ++nowt;
      change(d[nowt].pos,d[nowt].nowc);
    }
  while(nowt>q[x].t)
    {
      change(d[nowt].pos,d[nowt].prec);
      --nowt;
    }
  reverse(nowl,q[x].l);
  reverse(nowr,q[x].r);
  int f=lca(nowl=q[x].l,nowr=q[x].r);
  change(f);
  ans[q[x].id]=nowans;
  change(f);
  return 0;
}

int main()
{
  scanf("%d%d%d",&n,&m,&qq);
  for(register int i=1; i<=m; ++i)
    {
      scanf("%d",&val[i]);
    }
  for(register int i=1; i<=n; ++i)
    {
      scanf("%d",&cur[i]);
    }
  for(register int i=1; i<n; ++i)
    {
      int a,b;
      scanf("%d%d",&a,&b);
      ins(a,b);
      ins(b,a);
    }
  for(register int i=1; i<=n; ++i)
    {
      scanf("%d",&color[i]);
    }
  search();
  while(qq--)
    {
      int op,a,b;
      scanf("%d%d%d",&op,&a,&b);
      if(op)
        {
          if(dfn[a]>dfn[b])
            {
              std::swap(a,b);
            }
          q[++numq].l=a;
          q[numq].r=b;
          q[numq].t=numc;
          q[numq].id=numq;
        }
      else
        {
          d[++numc].pos=a;
          d[numc].prec=color[a];
          d[numc].nowc=color[a]=b;
        }
    }
  nowt=numc;
  nowans=0;
  std::sort(q+1,q+numq+1,cmp);
  while(nowt<q[1].t)
    {
      ++nowt;
      change(d[nowt].pos,d[nowt].nowc);
    }
  while(nowt>q[1].t)
    {
      change(d[nowt].pos,d[nowt].prec);
      --nowt;
    }
  reverse(q[1].l,q[1].r);
  int gg=lca(q[1].l,q[1].r);
  change(gg);
  ans[q[1].id]=nowans;
  change(gg);
  nowl=q[1].l;
  nowr=q[1].r;
  for(register int i=2; i<=numq; ++i)
    {
      solve(i);
    }
  for(register int i=1; i<=numq; ++i)
    {
      #ifdef ONLINE_JUDGE
      printf("%lld\n",ans[i]);
      #else
      printf("%I64d\n",ans[i]);
      #endif
    }
  return 0;
}

转载于:https://www.cnblogs.com/Canopus-wym/p/10376207.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值