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); } }