C74 动态DP+树剖 P5024 [NOIP2018 提高组] 保卫王国

视频链接:260 动态DP+树剖 P5024 [NOIP2018 提高组] 保卫王国_哔哩哔哩_bilibili

 

最小权点覆盖集 与 最大权独立集_harry1213812138的博客-CSDN博客

Luogu P5024 [NOIP2018 提高组] 保卫王国

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define int long long
#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
const int N=100005,inf=1e18;
int n,m,p[N],all;
vector<int>G[N];
int fa[N],siz[N],son[N],f[N][2];
int dfn[N],id[N],top[N],bot[N],tot;
//dfn:dfs序,id:节点编号,top:链头节点,bot:链尾序号

void dfs(int x){ //树剖f,fa,siz,son
  f[x][0]=0; f[x][1]=p[x];
  siz[x]=1;
  for(int y:G[x]){
    if(y==fa[x]) continue;
    fa[y]=x;
    dfs(y);
    f[x][0]+=max(f[y][0],f[y][1]);
    f[x][1]+=f[y][0];    
    siz[x]+=siz[y];
    if(siz[y]>siz[son[x]]) son[x]=y;
  }
}
void dfs(int x,int tp){ //树剖dfn,id,top,bot
  dfn[x]=++tot;id[tot]=x;top[x]=tp;bot[tp]=tot;
  if(son[x]) dfs(son[x],tp);
  for(auto y:G[x]){
    if(y==fa[x]||y==son[x]) continue;
    dfs(y,y);
  }
}

struct matrix{
  int g[2][2];
  matrix operator*(matrix b){ //广义矩阵乘积
    matrix t; 
    t.g[0][0]=t.g[0][1]=t.g[1][0]=t.g[1][1]=-inf;
    for(int i=0; i<=1; ++i)
    for(int j=0; j<=1; ++j)
    for(int k=0; k<=1; ++k)
      t.g[i][j]=max(t.g[i][j],g[i][k]+b.g[k][j]);
    return t;
  }  
}mt[N],tr[N<<2];//节点g矩阵,线段树g矩阵及乘积 

void build(int u,int l,int r){ //建线段树
  if(l==r){
    int x=id[l],g0=0,g1=p[x]; //g0不选x,g1选x
    for(auto y:G[x])
      if(y!=fa[x]&&y!=son[x])
        g0+=max(f[y][0],f[y][1]), //g0选或不选y
        g1+=f[y][0]; //g1不选y
    tr[u]=mt[x]={g0,g0,g1,-inf};
    return;
  }
  build(ls,l,mid);
  build(rs,mid+1,r);
  tr[u]=tr[ls]*tr[rs]; //g矩阵乘积
}
void change(int u,int l,int r,int p){ //点修
  if(l==r){tr[u]=mt[id[l]]; return;}
  if(p<=mid) change(ls,l,mid,p);
  else change(rs,mid+1,r,p);
  tr[u]=tr[ls]*tr[rs];
}
matrix query(int u,int l,int r,int x,int y){ //区查
  if(x==l&&r==y) return tr[u];
  if(y<=mid) return query(ls,l,mid,x,y);
  if(x>mid) return query(rs,mid+1,r,x,y);
  return query(ls,l,mid,x,mid)*query(rs,mid+1,r,mid+1,y);
}
void update(int u,int v){ //修改点权
  mt[u].g[1][0]+=v-p[u]; p[u]=v;
  while(u){
    matrix a=query(1,1,n,dfn[top[u]],bot[top[u]]);
    change(1,1,n,dfn[u]);
    matrix b=query(1,1,n,dfn[top[u]],bot[top[u]]);
    u=fa[top[u]]; //跳到链头的父节点
    mt[u].g[0][0]+=max(b.g[0][0],b.g[1][0])
                  -max(a.g[0][0],a.g[1][0]);
    mt[u].g[0][1]=mt[u].g[0][0];
    mt[u].g[1][0]+=b.g[0][0]-a.g[0][0];
  }
}
signed main(){
  scanf("%lld%lld",&n,&m);string s;cin>>s;
  for(int i=1;i<=n;i++)scanf("%lld",&p[i]),all+=p[i];
  for(int i=1,x,y;i<n;i++){
    scanf("%lld%lld",&x,&y);
    G[x].push_back(y); G[y].push_back(x);
  }
  dfs(1);dfs(1,1); //树链剖分
  build(1,1,n);    //建线段树
  while(m--){
    int a,x,b,y;
    scanf("%lld%lld%lld%lld",&a,&x,&b,&y);
    if(x==0&&y==0&&(fa[a]==b||fa[b]==a))
      {puts("-1"); continue;} //无解
    //覆盖集选则独立集不选,覆盖集不选则独立集必选      
    int va=p[a],vb=p[b];
    update(a,x?-inf:inf); update(b,y?-inf:inf);
    matrix t=query(1,1,n,dfn[1],bot[1]);
    int s=max(t.g[0][0],t.g[1][0]);
    s+=x?0:va-inf; 
    s+=y?0:vb-inf;
    printf("%lld\n",all-s);
    update(a,va); update(b,vb);
  }
}

 

posted @ 2023-12-03 18:57  董晓  阅读(129)  评论(0编辑  收藏  举报