动态dp模板题(树剖+dp+线段树)

动态最大带权独立集

(还有一个是全局平衡二叉树的解法,还没学)

#include"bits/stdc++.h"

using namespace std;
const int inf = 1e8;
int n,m;
int v[100005];
const int nn = 1e5+10;

int link[nn<<1],son[nn<<1],nxt[nn<<1];
int tot_;
int top[nn],deep[nn],wson[nn],fa[nn],size[nn],bot[nn];
int f[nn][2];
int dfn[nn];
int id[nn];
int tot;


struct Mat
{
   int l,r,a[3][3];
   Mat(int a1=0,int a2=0,int d1=0,int d2=0,int d3=0,int d4=0)
   {
      l=a1,r=a2;
      a[1][1]=d1,a[1][2] = d2;
      a[2][1] = d3,a[2][2] = d4;
   }


}tr[nn<<2];

Mat operator*(const Mat a,const Mat b)
{
    Mat re=Mat(2,2);


    re.a[1][1] = max(a.a[1][1]+b.a[1][1],a.a[1][2]+b.a[2][1]);
     re.a[1][2] = max(a.a[1][1]+b.a[1][2],a.a[1][2]+b.a[2][2]);

      re.a[2][1] = max(a.a[2][1]+b.a[1][1],a.a[2][2]+b.a[2][1]);
       re.a[2][2] = max(a.a[2][1]+b.a[1][2],a.a[2][2]+b.a[2][2]); // 手动

   return re;
}

inline int rd(){
    int x=0;char c=getchar();int neg=1;
    while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*neg;
}

inline void adde(int x,int y)
{
  ++tot_;
  nxt[tot_] = link[x];
  link[x] = tot_;
  son[tot_] = y;
}

void dfs1(int x,int ff,int dd)
{
  fa[x] =ff;
  size[x] =1;
  f[x][1] = v[x];
  f[x][0] = 0;

  for (int i=link[x];i;i=nxt[i])
  {
     int so = son[i];
     if (so==ff) continue;
     dfs1(so,x,dd+1);
     size[x] += size[so];
     if (size[wson[x]]<size[so]) wson[x] = so;
     f[x][1] += f[so][0];
     f[x][0] += max(f[so][1],f[so][0]);


  }

}

void dfs2(int x,int t)
{
   top[x] = t;
   dfn[x] = ++tot;
   id[tot] =x;

   if (!wson[x]) bot[top[x]] = x;//不要写成t
   else dfs2(wson[x],t);

   for ( int i=link[x];i;i=nxt[i])
   {
      int so =son[i];
      if (so==wson[x]||so==fa[x]) continue;
      dfs2(so,so);
   }
}


inline void change(int rt,int l,int r,int pos,int a,int b)
{
  if (l==r)
  {
     tr[rt].a[1][1]+=a; tr[rt].a[2][1]+=a;
     tr[rt].a[1][2]+=b;
     return ;
  }

  int mid = l+r>>1;
  if (pos<=mid) change(rt<<1,l,mid,pos,a,b);
  else change(rt<<1|1,mid+1,r,pos,a,b);
  tr[rt]=tr[rt<<1|1]*tr[rt<<1];//注意顺序
}


inline Mat query(int p,int l,int r,int x,int y){
    if(x<=l&&r<=y)
    {
    return tr[p];
    }
    else{
        int m=l+r>>1;Mat re=Mat(2,2,0,-inf,-inf,0);
        if(y>=m+1) re=re*query(p<<1|1,m+1,r,x,y);
        if(x<=m) re=re*query(p<<1,l,m,x,y);
        return re;
    }
}


inline void update(int x,int y)
{
   Mat nw,od;
 int a,b;
 while (x)
 { if (y) a=0,b=y,y=0;
  else a=max(nw.a[1][2],nw.a[1][1]) - max(od.a[1][2],od.a[1][1]),
  b = nw.a[1][1] - od.a[1][1];

  od = query(1,1,n,dfn[top[x]],dfn[bot[top[x]]]);
  change(1,1,n,dfn[x],a,b);
  nw = query(1,1,n,dfn[top[x]],dfn[bot[top[x]]]);
  x=fa[top[x]];
  }

  printf("%d\n",max(nw.a[1][1],nw.a[1][2]));
}

void build (int rt,int l,int r)
{
  if (l==r)
  {
     int zhi = id[l];
     int a = f[zhi][0] - max(f[wson[zhi]][0],f[wson[zhi]][1]);
     int b = f[zhi][1] - f[wson[zhi]][0];
     tr[rt] = Mat(2,2,a,b,a,-1e8);
     return ;
  }

  int mid = l+r>>1;
  build(rt<<1,l,mid);
  build(rt<<1|1,mid+1,r);
  tr[rt]=tr[rt<<1|1]*tr[rt<<1];
}

int main()
{
  n=rd(); m = rd();
  for ( int i=1;i<=n;i++) v[i] = rd();

  for ( int i =1;i<n;i++)
  {
     int a,b;a=rd(); b = rd();
     adde(a,b);
     adde(b,a);
  }
  dfs1(1,0,1);
  dfs2(1,1);

  build(1,1,n);
  //puts("$%^&*");

  for ( int i=1;i<=m;i++)

  {
   //  puts("123f");
     int a,b;a=rd();  b=rd();

     update(a,b-v[a]);
   //  puts("213");
     v[a] =b;
  }
  return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值