n个点的树,每个点选的代价为val[i],m个询问,问确定某两个点选或不选后树的最小点覆盖。n,m<=100000
最小点覆盖,设 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示 i i i点不选/选时子树的最小代价,转移为:
{ f [ u ] [ 0 ] = ∑ v f [ v ] [ 1 ] f [ u ] [ 1 ] = v a l [ u ] + ∑ v min ( f [ v ] [ 0 ] , f [ v ] [ 1 ] ) \begin{cases}f[u][0]=\sum_vf[v][1]\\f[u][1]=val[u]+\sum_v\min(f[v][0],f[v][1])\end{cases} {f[u][0]=∑vf[v][1]f[u][1]=val[u]+∑vmin(f[v][0],f[v][1])
然后剩下就是动态DP的事情了,可以类比动态DP【模板】
如果强制要求选1就把 f [ 1 ] + = − I N F f[1]+=-INF f[1]+=−INF,最后再加回来,如果要求选0就把 f [ 1 ] + = I N F f[1]+=INF f[1]+=INF就是正常的动态DP了。
对于此题为了方便使链末尾的点的转移矩阵恰好对应它的 f f f,可以这样设置:
[ f [ 1 ] f [ 0 ] ] = [ f ′ [ 1 ] f ′ [ 1 ] f ′ [ 0 ] I N F ] ∗ [ f s o n [ 1 ] f s o n [ 0 ] ] \begin{bmatrix}f[1]\\f[0]\end{bmatrix}=\begin{bmatrix}f'[1] & f'[1]\\f'[0] & INF\end{bmatrix}*\begin{bmatrix}f_{son}[1]\\f_{son}[0]\end{bmatrix} [f[1]f[0]]=[f′[1]f′[0]f′[1]INF]∗[fson[1]fson[0]]
f ′ [ 0 / 1 ] f'[0/1] f′[0/1]表示用只用轻儿子转移时的 f f f值。
如果将1,0反过来的话就会发现转移矩阵变成了 [ I N F f ′ [ 0 ] f ′ [ 1 ] f ′ [ 1 ] ] \begin{bmatrix}INF & f'[0]\\f'[1] & f'[1]\end{bmatrix} [INFf′[1]f′[0]f′[1]],此时链末尾的左半部分就不与它的f矩阵等价了,但是你可以给末尾乘上一个 [ I N F 0 ] \begin{bmatrix}INF\\0\end{bmatrix} [INF0],这样它就等价了,你会发现这时转移矩阵的乘积(未乘0矩阵)的右半部分才是我们想要的 f f f矩阵。
LCT一开始所有儿子都是轻儿子。矩阵拆开写可以优化常数。
Code:
#include
#define maxn 100005
#define LL long long
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
inline void write(LL x){
if(x>=10) write(x/10);
putchar(x%10+48);
}
const LL inf = 1ll<<50;
struct Mat{
LL s[2][2];
Mat(){s[0][0]=s[0][1]=s[1][0]=s[1][1]=inf;}
void init(LL *f){s[0][0]=s[0][1]=f[1],s[1][0]=f[0],s[1][1]=inf;}
LL Min(){return min(s[0][0],s[1][0]);}
Mat operator * (const Mat &B)const{
Mat ret;
ret.s[0][0]=min(s[0][0]+B.s[0][0],s[0][1]+B.s[1][0]);
ret.s[0][1]=min(s[0][0]+B.s[0][1],s[0][1]+B.s[1][1]);
ret.s[1][0]=min(s[1][0]+B.s[0][0],s[1][1]+B.s[1][0]);
ret.s[1][1]=min(s[1][0]+B.s[0][1],s[1][1]+B.s[1][1]);
return ret;
}
};
namespace LCT{
int ch[maxn][2],fa[maxn];
LL f[maxn][2]; Mat g[maxn];
#define il inline
#define pa fa[x]
il bool isr(int x){return ch[pa][0]!=x&&ch[pa][1]!=x;}
il bool isc(int x){return ch[pa][1]==x;}
il void upd(int x){g[x].init(f[x]),g[x]=g[ch[x][0]]*g[x]*g[ch[x][1]];}
il void rot(int x){
int y=fa[x],z=fa[y],c=isc(x);
!isr(y)&&(ch[z][isc(y)]=x);
(ch[y][c]=ch[x][!c])&&(fa[ch[y][c]]=y);
fa[ch[x][!c]=y]=x,fa[x]=z;
upd(y),upd(x);
}
il void splay(int x){
for(;!isr(x);rot(x))
if(!isr(pa)) rot(isc(pa)==isc(x)?pa:x);
}
il void access(int x){
for(int y=0;x;x=fa[y=x]){
splay(x);
f[x][0]+=g[ch[x][1]].s[0][0]-g[y].s[0][0];
f[x][1]+=g[ch[x][1]].Min()-g[y].Min();
ch[x][1]=y,upd(x);
}
}
il void modify(int x,bool v){access(x),splay(x),f[x][1]+=v?-inf:inf,upd(x);}
}
using namespace LCT;
int n,m,val[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
void dfs(int u,int ff){
fa[u]=ff,f[u][1]=val[u];
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
dfs(v,u);
f[u][0]+=f[v][1];
f[u][1]+=min(f[v][0],f[v][1]);
}
g[u].init(f[u]);
}
int main()
{
g[0].s[0][0]=g[0].s[1][1]=0;
int a,x,b,y;
read(n),read(m),read(x);
for(int i=1;i<=n;i++) read(val[i]);
for(int i=1;i<n;i++) read(x),read(y),line(x,y),line(y,x);
dfs(1,0);
while(m--){
read(a),read(x),read(b),read(y);
modify(a,x),modify(b,y),splay(1);
LL ans=g[1].Min()+(x+y)*inf;
if(ans>=inf) puts("-1");
else write(ans),putchar('\n');
modify(a,!x),modify(b,!y);
}
}