标签:去掉 获得 cto href struct math 形式 bsp ack
动态dp是一个毒瘤且奇葩的东西...
然而noip2018出了这个东西...
因此...
以一道题为例吧:给出一棵带点权的树,每次修改一个点的点权,查询这棵树的最大权独立集(可以理解为每次询问一遍“没有上司的舞会”)
首先考虑暴力:
设状态$f[i][0/1]$表示以$i$为根的子树,点$i$选或不选所能获得的最大权独立集权值,设$i$点权值为$w_{i}$,显然有转移:
$f[i][0]=\sum max(f[son][0],f[son][1])$
$f[i][1]=w_{i}+\sum f[son][0]$
显然这样转移每次都是$O(n)$的,复杂度爆炸
考虑每次修改会影响的范围:显然,每次修改只会影响从这个点到根节点的一条树链,那么我们考虑是否可以用树链剖分来进行优化
树链剖分显然需要套个数据结构维护,考虑线段树
(这里用LCT+SPLAY好像也可以,但是我不想写...)
怎么维护?
线段树支持单点修改,区间查询,但是...查什么呢?
看到这是一个递推的形式,考虑线段树维护转移矩阵
喂,这玩意怎么写转移矩阵嘛!
考虑借助一下东方的神秘力量其他变量
记$g[i][0]$表示以$i$为根的子树中所有非重儿子任意选取的价值之和,即$g[i][0]=f[i][0]-max(f[son_{i}][0],f[son_{i}][1])$
记$g[i][1]$表示以$i$为根的子树中所有非重儿子必须不选的价值与该节点权值之和,即$g[i][1]=f[i][1]-f[son_{i}][0]$
($son_{i}$表示$i$的重儿子,$g$也就是$f$去掉了重儿子的贡献)
给出这两个定义之后就可以重写转移方程了:
$f[i][0]=g[i][0]+max(f[son_{i}][0],f[son_{i}][1])$
$f[i][1]=g[i][1]+f[son_{i}][0]$
这么倒腾半天有啥用?
把两个转移方程都写成直接取最大值的形式,就是:
$f[i][0]=max(g[i][0]+f[son_{i}][0],g[i][0]+f[son_{i}][1])$
$f[i][1]=max(g[i][1]+f[son_{i}][0],-\infty)$
(后面那个$\infty$怕不是打酱油的...)
这又有啥用?能变成矩阵吗?
好像不能,但是...如果我们重新定义矩阵乘法的运算规则呢?
定义矩阵乘法$A*B=C$,运算规则如下:
$C_{ij}=(max_{k=1}^{n}A_{ik}+B_{kj})$(表示对后面那一堆取最大值)
这又有啥用?
这样就可以把转移写成矩阵形式了!
$\begin{pmatrix}f[i][0] \\f[i][1]\end{pmatrix}$ $=$ $\begin{pmatrix} g[i][0] & g[i][0] \\ g[i][1 ]& -\infty \end{pmatrix}$ $\begin{pmatrix} f[son_{i}][0]\\f[son_{i}][1] \end{pmatrix}$
那么如果这个东西满足结合律,就可以直接上线段树维护了
瞎**YY一下,这个东西满足结合律!
那么上线段树维护即可
考虑怎么维护:
每修改一个点时,我们事实上修改的是这个点对应的转移矩阵中的$g[i][1]$!
因此直接修改即可
再考虑修改一个点对上面的点的影响:
首先对这条重链上的点没有影响,因为$g$记录的都是轻儿子的状态
然后会对这条重链链顶的父亲有影响,因此每次跳到链顶的父亲即可(注意到重链的链顶一定是自己父亲的轻儿子)
考虑有什么影响:修改一个点会影响这个点所对应的链顶的$f$值,而计算一个链顶的$f$其实就是把这条重链上每个点维护的转移矩阵乘起来!
因此我们在修改时计算修改前后这个链顶的$f$的差值,也就算出了链顶对其父节点的影响了
这样向上跳跃更新即可
时间复杂度$O(nlog_{2}n^{2})$
贴代码:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <vector> #define rt1 rt<<1 #define rt2 (rt<<1)|1 #define ll long long using namespace std; const ll inf=0x3f3f3f3f3f3f3f3fll; struct MAT { ll a[2][2]; friend MAT operator * (MAT x,MAT y) { MAT ret; ret.a[0][0]=max(x.a[0][0]+y.a[0][0],x.a[0][1]+y.a[1][0]); ret.a[0][1]=max(x.a[0][1]+y.a[1][1],x.a[0][0]+y.a[0][1]); ret.a[1][0]=max(x.a[1][0]+y.a[0][0],x.a[1][1]+y.a[1][0]); ret.a[1][1]=max(x.a[1][1]+y.a[1][1],x.a[1][0]+y.a[0][1]); return ret; } }ori[100005]; MAT tree[400005]; vector <int> v[100005]; int ttop[100005],dep[100005],f[100005],siz[100005],son[100005],ed[100005]; int nnum[100005],onum[100005]; int tot=0; ll w[100005],F[100005][2]; int n,m; void dfs(int x,int fx) { siz[x]=1,dep[x]=dep[fx]+1,f[x]=fx; for(int i=0;i<v[x].size();i++) { int to=v[x][i]; if(to==fx)continue; dfs(to,x); siz[x]+=siz[to]; if(siz[son[x]]<siz[to])son[x]=to; } } void redfs(int x,int fx,int topx) { ttop[x]=topx,nnum[x]=++tot,onum[tot]=x; if(son[x])redfs(son[x],x,topx),ed[x]=ed[son[x]]; else ed[x]=x; for(int i=0;i<v[x].size();i++) { int to=v[x][i]; if(to==fx||to==son[x])continue; redfs(to,x,to); } } void tree_dp(int x,int fx) { F[x][1]=w[x]; for(int i=0;i<v[x].size();i++) { int to=v[x][i]; if(to==fx)continue; tree_dp(to,x); F[x][1]+=F[to][0],F[x][0]+=max(F[to][0],F[to][1]); } } void buildtree(int rt,int l,int r) { if(l==r) { int p=onum[l]; ll g0=F[p][0]-max(F[son[p]][1],F[son[p]][0]),g1=F[p][1]-F[son[p]][0]; ori[p].a[0][0]=ori[p].a[0][1]=g0; ori[p].a[1][0]=g1,ori[p].a[1][1]=-inf; tree[rt]=ori[p]; return; } int mid=(l+r)>>1; buildtree(rt1,l,mid),buildtree(rt2,mid+1,r); tree[rt]=tree[rt1]*tree[rt2]; } void update(int rt,int l,int r,int posi) { if(l==r){tree[rt]=ori[onum[l]];return;} int mid=(l+r)>>1; if(posi<=mid)update(rt1,l,mid,posi); else update(rt2,mid+1,r,posi); tree[rt]=tree[rt1]*tree[rt2]; } MAT query(int rt,int l,int r,int lq,int rq) { if(l>=lq&&r<=rq)return tree[rt]; int mid=(l+r)>>1; if(rq<=mid)return query(rt1,l,mid,lq,rq); else if(lq>mid)return query(rt2,mid+1,r,lq,rq); else return query(rt1,l,mid,lq,rq)*query(rt2,mid+1,r,lq,rq); } void ins(int p,ll t) { ori[p].a[1][0]+=t-w[p],w[p]=t; while(p) { MAT m0=query(1,1,n,nnum[ttop[p]],nnum[ed[p]]); update(1,1,n,nnum[p]); MAT m1=query(1,1,n,nnum[ttop[p]],nnum[ed[p]]); p=f[ttop[p]]; if(!p)break; ori[p].a[0][0]+=max(m1.a[0][0],m1.a[1][0])-max(m0.a[0][0],m0.a[1][0]); ori[p].a[0][1]=ori[p].a[0][0]; ori[p].a[1][0]+=m1.a[0][0]-m0.a[0][0]; } } template <typename T>inline void read(T &x) { T f=1,c=0;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){c=c*10+ch-‘0‘;ch=getchar();} x=c*f; } int main() { read(n),read(m); for(int i=1;i<=n;i++)read(w[i]); for(int i=1;i<n;i++) { int x,y; read(x),read(y); v[x].push_back(y),v[y].push_back(x); } dfs(1,0),redfs(1,0,1),tree_dp(1,1),buildtree(1,1,n); while(m--) { int p;ll t; read(p),read(t); ins(p,t); MAT ret=query(1,1,n,nnum[1],nnum[ed[1]]); printf("%lld\n",max(ret.a[0][0],ret.a[1][0])); } return 0; }
标签:去掉 获得 cto href struct math 形式 bsp ack
原文地址:https://www.cnblogs.com/zhangleo/p/11091300.html