[復習]動態dp
你還是可以認為我原來寫的動態dp就是在扯蛋。
[Luogu4719]【模板】動態dp
首先作為一個\(dp\)題,我們顯然可以每次修改之后都進行暴力\(dp\),設\(f[i][0/1]\)表示當前考慮\(i\)及其子樹內的點,當前這個點是選還是不選時能夠得到的最大權值,那么我們可以得到轉移:\(f[i][0]+=\max\{f[v][0],f[v][1]\},f[i][1]+=f[v][0]\),其中\(v\)是\(i\)的一個兒子。
那么這樣子的復雜度就是\(O(qn)\)。
仔細想想我們這樣子為什么慢?因為我們重復計算了大量相同的轉移,那么我們發現我們可以每次只需要修改當前的修改點到達根節點的這條鏈的所有點的\(dp\)值就好了,然而這樣子復雜度最差還是\(O(nq)\)的。
那么我們還有哪里慢呢?
仔細想想,我們的轉移都是一模一樣的,區別只是在於每次的取值不同而已。
先考慮一模一樣的轉移可以怎么處理,似乎可以矩陣乘法?那么轉移可以寫成:
然而這樣子的轉移只有在這個點轉移第一個兒子的時候是對的。
如果有多個兒子的話,我們把\(V_u\)看成除了這個兒子之外的\(f[u][1]\),\(0\)看成除了這個兒子之外的\(f[u][0]\)。
那么等價於是我們要把兒子給分成兩個部分,第一部分表示的是這個特殊轉移的兒子,第二部分是剩下的兒子。似乎可以重鏈剖分?
那么對於重兒子我們可以直接維護這個矩陣,這樣子用線段樹維護矩陣乘法就可以算出重兒子部分的貢獻,其他輕兒子暴力往上貢獻。
看起來這個東西復雜度就很對了啊,單次修改只會造成\(log\)次暴力修改。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 100100
#define lson (now<<1)
#define rson (now<<1|1)
const ll inf=1e17;
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,Q,V[MAX];
int fa[MAX],top[MAX],bot[MAX],size[MAX],hson[MAX],dfn[MAX],tim,ln[MAX];
void dfs1(int u,int ff)
{
fa[u]=ff;size[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
dfs1(v,u);size[u]+=size[v];
if(size[v]>size[hson[u]])hson[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;dfn[u]=++tim,ln[tim]=u;
if(hson[u])dfs2(hson[u],tp),bot[u]=bot[hson[u]];
else bot[u]=u;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=fa[u]&&e[i].v!=hson[u])
dfs2(e[i].v,e[i].v);
}
ll f[MAX][2];
void dfs(int u,int ff)
{
f[u][0]=0;f[u][1]=V[u];
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
dfs(v,u);
f[u][0]+=max(f[v][0],f[v][1]);
f[u][1]+=f[v][0];
}
}
struct Matrix
{
ll a[2][2];
ll*operator[](int x){return a[x];}
}t[MAX<<2],tmp[MAX];
Matrix operator*(Matrix a,Matrix b)
{
Matrix c;
c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]);
c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]);
c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]);
c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]);
return c;
}
void Build(int now,int l,int r)
{
if(l==r)
{
int u=ln[l];ll g0=0,g1=V[u];
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=fa[u]&&e[i].v!=hson[u])
g0+=max(f[e[i].v][0],f[e[i].v][1]),g1+=f[e[i].v][0];
tmp[l]=t[now]=(Matrix){g0,g0,g1,-inf};
return;
}
int mid=(l+r)>>1;
Build(lson,l,mid);Build(rson,mid+1,r);
t[now]=t[lson]*t[rson];
}
void Modify(int now,int l,int r,int p)
{
if(l==r){t[now]=tmp[l];return;}
int mid=(l+r)>>1;
if(p<=mid)Modify(lson,l,mid,p);
else Modify(rson,mid+1,r,p);
t[now]=t[lson]*t[rson];
}
Matrix Query(int now,int l,int r,int L,int R)
{
if(L==l&&r==R)return t[now];
int mid=(l+r)>>1;
if(R<=mid)return Query(lson,l,mid,L,R);
if(L>mid)return Query(rson,mid+1,r,L,R);
return Query(lson,l,mid,L,mid)*Query(rson,mid+1,r,mid+1,R);
}
void Modify(int u,int w)
{
tmp[dfn[u]][1][0]+=w-V[u];V[u]=w;
while(u)
{
Matrix a=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
Modify(1,1,n,dfn[u]);
Matrix b=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
u=fa[top[u]];if(!u)break;int x=dfn[u];
ll g0=a[0][0],g1=a[1][0],f0=b[0][0],f1=b[1][0];
tmp[x][0][0]=tmp[x][0][1]=tmp[x][0][0]+max(f0,f1)-max(g0,g1);
tmp[x][1][0]=tmp[x][1][0]+f0-g0;
}
}
int main()
{
n=read();Q=read();
for(int i=1;i<=n;++i)V[i]=read();
for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
dfs1(1,0);dfs2(1,1);dfs(1,0);Build(1,1,n);
while(Q--)
{
int x=read(),w=read();Modify(x,w);
Matrix ans=Query(1,1,n,dfn[1],dfn[bot[1]]);
printf("%lld\n",max(ans[0][0],ans[1][0]));
}
return 0;
}
[NOIP2018]保衛王國
要求最小點覆蓋。這個東西等於權值和減去最大獨立集。
而最大獨立集就是上面的那個東西。
考慮如何強制某個點必須選或者不選,如果必須選則將其權值減去一個\(inf\),如果必須不選則將其權值加上一個\(inf\)。
然后就做完了。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 100100
#define lson (now<<1)
#define rson (now<<1|1)
const ll inf=1e17;
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,Q;ll V[MAX];
int fa[MAX],top[MAX],bot[MAX],size[MAX],hson[MAX],dfn[MAX],tim,ln[MAX];
void dfs1(int u,int ff)
{
fa[u]=ff;size[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
dfs1(v,u);size[u]+=size[v];
if(size[v]>size[hson[u]])hson[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;dfn[u]=++tim,ln[tim]=u;
if(hson[u])dfs2(hson[u],tp),bot[u]=bot[hson[u]];
else bot[u]=u;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=fa[u]&&e[i].v!=hson[u])
dfs2(e[i].v,e[i].v);
}
ll f[MAX][2];
void dfs(int u,int ff)
{
f[u][0]=0;f[u][1]=V[u];
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
dfs(v,u);
f[u][0]+=max(f[v][0],f[v][1]);
f[u][1]+=f[v][0];
}
}
struct Matrix
{
ll a[2][2];
ll*operator[](int x){return a[x];}
}t[MAX<<2],tmp[MAX];
Matrix operator*(Matrix a,Matrix b)
{
Matrix c;
c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]);
c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]);
c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]);
c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]);
return c;
}
void Build(int now,int l,int r)
{
if(l==r)
{
int u=ln[l];ll g0=0,g1=V[u];
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=fa[u]&&e[i].v!=hson[u])
g0+=max(f[e[i].v][0],f[e[i].v][1]),g1+=f[e[i].v][0];
tmp[l]=t[now]=(Matrix){g0,g0,g1,-inf};
return;
}
int mid=(l+r)>>1;
Build(lson,l,mid);Build(rson,mid+1,r);
t[now]=t[lson]*t[rson];
}
void Modify(int now,int l,int r,int p)
{
if(l==r){t[now]=tmp[l];return;}
int mid=(l+r)>>1;
if(p<=mid)Modify(lson,l,mid,p);
else Modify(rson,mid+1,r,p);
t[now]=t[lson]*t[rson];
}
Matrix Query(int now,int l,int r,int L,int R)
{
if(L==l&&r==R)return t[now];
int mid=(l+r)>>1;
if(R<=mid)return Query(lson,l,mid,L,R);
if(L>mid)return Query(rson,mid+1,r,L,R);
return Query(lson,l,mid,L,mid)*Query(rson,mid+1,r,mid+1,R);
}
void Modify(int u,ll w)
{
tmp[dfn[u]][1][0]+=w-V[u];V[u]=w;
while(u)
{
Matrix a=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
Modify(1,1,n,dfn[u]);
Matrix b=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
u=fa[top[u]];if(!u)break;int x=dfn[u];
ll g0=a[0][0],g1=a[1][0],f0=b[0][0],f1=b[1][0];
tmp[x][0][0]=tmp[x][0][1]=tmp[x][0][0]+max(f0,f1)-max(g0,g1);
tmp[x][1][0]=tmp[x][1][0]+f0-g0;
}
}
int main()
{
n=read();Q=read();scanf("%*s");ll sum=0;
for(int i=1;i<=n;++i)sum+=(V[i]=read());
for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
dfs1(1,0);dfs2(1,1);dfs(1,0);Build(1,1,n);
while(Q--)
{
int a=read(),x=read(),b=read(),y=read(),pa=V[a],pb=V[b];
ll pls=0;
if(!x)Modify(a,pa+inf),pls+=inf;else Modify(a,pa-inf);
if(!y)Modify(b,pb+inf),pls+=inf;else Modify(b,pb-inf);
Matrix ret=Query(1,1,n,dfn[1],dfn[bot[1]]);
ll ans=max(ret[0][0],ret[1][0])-pls;
if(ans<0)puts("-1");
else printf("%lld\n",sum-ans);
Modify(a,pa);Modify(b,pb);
}
return 0;
}