题解 P4751 【动态dp【加强版】】
Great_Influence
2018-08-12 22:04:09
你们可以看看这个[宝贝](https://www.cnblogs.com/RabbitHu/p/9112811.html)来了解一下这个诡异的做法。
首先,需要前置技能[动态dp](https://www.luogu.org/problemnew/show/P4719),然后才能够做这道题。
很明显,利用树剖套线段树直接维护矩阵的时间复杂度是$O(2^3qlog^2n)$的。再数据恶意卡的情况下,计算出来的复杂度高达$1e9$级,再算上线段树的巨大常数,基本上直接就$TLE$了。这时,我们需要一种更靠谱的复杂度来维护。
根据经验,在维护内容简单的情况下,如果某个信息可以用$splay$做到和线段树同级的维护,那么在$LCT$的意义下,是可以将两个$log$优化到一个$log$的。
那么我们的基本思路就出来了:就是不使用树剖套线段树来维护矩阵,而是改为使用$LCT$来维护矩阵。这样的时间复杂度就由$O(2^3q\log^2 n)$优化至$O(2^3q\log n)$。
不过$LCT$的常数过于巨大,并且树的形态固定,我们也不需要动态来维护一棵树。我们可以使用与$LCT$类似的数据结构来维护,同时也保证它的复杂度。
我们采用一棵普通的二叉搜索树来维护这个结构。其结构与$LCT$相似,存在轻边和重边之分,且一段重链用相连的一段二叉搜索树维护,而轻边直接用轻边挂在父亲节点下。这样,我们只要保证这个结构的深度不超过$O(\log n)$级,我们的复杂度就是正确的。
这时就有一个很简单的构造思想了。先将整棵树树链剖分(重链剖分),再对于每个点,记录该点的轻子树的$size$之和+1,作为他的权。然后对于当前重链,记录它的加权重心,作为该重链的根,然后向两边递归建树。轻边还是一样挂在其父亲上。这样可以将树高严格卡在$O(\log n)$内。证明非常简单,每个点的父亲子树大小**至少是它自己子树大小的2倍**。这样经过不超过$\log n$次后,其子树大小会变成$n$。
代码(仅供参考):
```cpp
#include<bits/stdc++.h>
#define Rep(i,a,b) for(register int i=(a);i<=(b);++i)
#define Repe(i,a,b) for(register int i=(a);i>=(b);--i)
#define pb push_back
#define Chkmax(a,b) a=a>b?a:b
#define Chkmin(a,b) a=a<b?a:b
#define mx(a,b) (a>b?a:b)
typedef unsigned long long uint64;
typedef unsigned int uint32;
typedef long long ll;
using namespace std;
namespace IO
{
const uint32 Buffsize=1<<15,Output=1<<24;
static char Ch[Buffsize],*St=Ch,*T=Ch;
inline char getc()
{
return((St==T)&&(T=(St=Ch)+fread(Ch,1,Buffsize,stdin),St==T)?0:*St++);
}
static char Out[Output],*nowps=Out;
inline void flush(){fwrite(Out,1,nowps-Out,stdout);nowps=Out;}
template<typename T>inline void read(T&x)
{
x=0;static char ch;T f=1;
for(ch=getc();!isdigit(ch);ch=getc())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getc())x=x*10+(ch^48);
x*=f;
}
template<typename T>inline void write(T x,char ch='\n')
{
if(!x)*nowps++=48;
if(x<0)*nowps++='-',x=-x;
static uint32 sta[111],tp;
for(tp=0;x;x/=10)sta[++tp]=x%10;
for(;tp;*nowps++=sta[tp--]^48);
*nowps++=ch;
}
}
using namespace IO;
inline void file()
{
#ifndef ONLINE_JUDGE
freopen("Ddp++.in","r",stdin);
freopen("Ddp++.out","w",stdout);
#endif
}
const int inf=0x3f3f3f3f,MAXN=1e6+7;
struct matrix
{
int s[2][2];
matrix(){s[0][0]=s[0][1]=s[1][0]=s[1][1]=-inf;}
matrix(int x){s[0][0]=s[1][1]=0;s[0][1]=s[1][0]=-inf;}
friend matrix operator*(matrix a,matrix b)
{
matrix c;
Chkmax(c.s[0][0],b.s[0][0]+a.s[0][0]);
Chkmax(c.s[0][0],b.s[0][1]+a.s[1][0]);
Chkmax(c.s[0][1],b.s[0][0]+a.s[0][1]);
Chkmax(c.s[0][1],b.s[0][1]+a.s[1][1]);
Chkmax(c.s[1][0],b.s[1][0]+a.s[0][0]);
Chkmax(c.s[1][0],b.s[1][1]+a.s[1][0]);
Chkmax(c.s[1][1],b.s[1][0]+a.s[0][1]);
Chkmax(c.s[1][1],b.s[1][1]+a.s[1][1]);
return c;
}
inline int getmx()
{return mx(mx(s[0][0],s[0][1]),mx(s[1][0],s[1][1]));}
};
static int n,m;
static int sz[MAXN],son[MAXN],g[MAXN][2],w[MAXN];
static struct edge
{
int v,nxt;
}p[MAXN<<1];
static int head[MAXN],e;
inline void add(int u,int v){p[++e]={v,head[u]};head[u]=e;}
void dfs(int u)
{
sz[u]=1;g[u][1]=w[u];
for(register int v=head[u];v;v=p[v].nxt)if(!sz[p[v].v])
{
dfs(p[v].v);sz[u]+=sz[p[v].v];
if(sz[p[v].v]>sz[son[u]])son[u]=p[v].v;
g[u][0]+=mx(g[p[v].v][0],g[p[v].v][1]);
g[u][1]+=g[p[v].v][0];
}
}
namespace bst
{
static matrix W[MAXN],S[MAXN];
static int so[MAXN][2],fa[MAXN],sta[MAXN],tp;
static int ssz[MAXN],rt;
inline void update(int u){S[u]=S[so[u][0]]*W[u]*S[so[u][1]];}
int build_tree(int l,int r)
{
if(l>r)return 0;
static int tot;tot=0;Rep(i,l,r)tot+=ssz[sta[i]];
for(register int i=l,cnt=ssz[sta[i]];i<=r;++i,cnt+=ssz[sta[i]])
if(cnt*2>=tot)
{
int rs=build_tree(l,i-1),ls=build_tree(i+1,r);
so[sta[i]][0]=ls,so[sta[i]][1]=rs;
fa[ls]=fa[rs]=sta[i];update(sta[i]);
return sta[i];
}
}
inline void getpoi(int u)
{W[u].s[0][0]=W[u].s[0][1]=**(g+u);W[u].s[1][0]=*(*(g+u)+1);}
int build(int top,int fr)
{
for(int t=top;t;fr=t,t=son[t])
{
for(register int v=head[t];v;v=p[v].nxt)
if(p[v].v^son[t]&&p[v].v^fr)
fa[build(p[v].v,t)]=t;
getpoi(t);
}
tp=0;
for(int t=top;t;t=son[t])
sta[++tp]=t,ssz[t]=sz[t]-sz[son[t]];
return build_tree(1,tp);
}
bitset<MAXN>isr;
void modify(int u,int val)
{
g[u][1]+=val-w[u];
w[u]=val;
static int dp0[2],dp1[2];
for(;u;u=fa[u])
{
dp0[0]=mx(S[u].s[0][0],S[u].s[0][1]);
dp0[1]=mx(S[u].s[1][0],S[u].s[1][1]);
getpoi(u);update(u);
dp1[0]=mx(S[u].s[0][0],S[u].s[0][1]);
dp1[1]=mx(S[u].s[1][0],S[u].s[1][1]);
if(isr.test(u))
{
g[fa[u]][0]+=mx(dp1[0],dp1[1])-mx(dp0[0],dp0[1]);
g[fa[u]][1]+=dp1[0]-dp0[0];
}
}
}
}
void dfs(int u,int fr)
{
if(!son[u])return;
g[u][0]-=mx(g[son[u]][0],g[son[u]][1]);
g[u][1]-=g[son[u]][0];
for(register int v=head[u];v;v=p[v].nxt)if(p[v].v^fr)dfs(p[v].v,u);
}
using namespace bst;
inline void init()
{
read(n);read(m);
Rep(i,1,n)read(w[i]);
static int u,v;
Rep(i,1,n-1)read(u),read(v),add(u,v),add(v,u);
dfs(1);
dfs(1,0);
W[0]=S[0]=matrix(0);
rt=build(1,0);
Rep(i,1,n)if(i^so[fa[i]][0]&&i^so[fa[i]][1])isr.set(i);
}
inline void solve()
{
static int lasans=0;
static int x,y;
Rep(i,1,m)
{
read(x);read(y);
x^=lasans;
modify(x,y);
write(lasans=S[rt].getmx());
}
flush();
}
int main()
{
file();
init();
solve();
cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
return 0;
}
```