CF1092F
典。参见 P2986。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,tot,a[N];
vector<int> G[N<<1];
int dp[N],f[N],siz[N];
void dfs1(int cur,int fa){
siz[cur]=a[cur],dp[cur]=0;
for(int i:G[cur]){
if(i==fa) continue;
dfs1(i,cur);
siz[cur]+=siz[i];
dp[cur]+=dp[i]+siz[i];
}
}
void dfs2(int cur,int fa){
for(int i:G[cur]){
if(i==fa) continue;
f[i]=f[cur]+tot-2*siz[i];
dfs2(i,cur);
}
}
signed main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],tot+=a[i];
for(int i=1,u,v;i<n;i++)
cin>>u>>v,
G[u].push_back(v),
G[v].push_back(u);
dfs1(1,0);
f[1]=dp[1];
dfs2(1,0);
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,f[i]);
cout<<ans;
return 0;
}
CF490F
因为 LIS 可能跑到子树外去,而当某节点作为根时就不存在子树外了,考虑换根。
令 \(dp_{cur}\) 表示以 \(cur\) 为根的子树内且以 \(cur\) 结尾的 LIS 的长度。
初始:\(dp_{cur}=1\)。
转移时朴素地在以子节点的子树中寻找满足 \(a_p<a_{cur}\) 的节点 \(p\) 进行转移即可:
令 \(f_{cur}\) 表示以 \(cur\) 为全局根节点且以 \(cur\) 结尾的 LIS 的长度。
初始:\(f_{cur}=dp_{cur}\)。
钦定 \(nxt\) 为 \(cur\) 的一个子节点。
转移时朴素地在除 \(nxt\) 子树外的部分单独求一遍贡献(\(dp\) 值),然后与 \(f_{nxt}\) 取 \(\max\) 即可。具体实现见代码。
注意单独求完子树外的部分后要重新赋 \(dp\) 值(常规特殊处理)。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=6e3+5;
int n,ans,sum;
vector<int> G[N<<1];
int a[N],dp[N],f[N];
void dfs_son(int cur,int fa,int o){
if(a[cur]<a[o])
sum=max(sum,dp[cur]+1);
for(int i:G[cur]){
if(i==fa) continue;
dfs_son(i,cur,o);
}
}
void dfs1(int cur,int fa){
dp[cur]=1;
for(int i:G[cur]){
if(i==fa) continue;
dfs1(i,cur);
sum=0;
dfs_son(i,cur,cur);
dp[cur]=max(dp[cur],sum);
}
}
void dfs2(int cur,int fa){
for(int i:G[cur]){
if(i==fa) continue;
int t=dp[cur];
sum=0;
for(int j:G[cur])
if(j!=i)
dfs_son(j,cur,cur);
dp[cur]=max(1ll,sum);
sum=0;
int tt=dp[i];
dfs_son(cur,i,i);
dp[i]=max(dp[i],sum);
ans=max(ans,dp[i]);
dfs2(i,cur);
dp[cur]=t,dp[i]=tt;
}
}
signed main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1,u,v;i<n;i++)
cin>>u>>v,
G[u].push_back(v),
G[v].push_back(u);
dfs1(1,0);
ans=dp[1];
dfs2(1,0);
cout<<ans;
return 0;
}
P3047
与某个节点相距 \(k\) 个单位的节点可能在子树内或子树外,于是考虑换根。
令 \(dp_{cur,j}\) 表示以 \(cur\) 为根的子树内距它不超过 \(j\) 个单位的节点权值和(题目有一个距离的约束,因此要设两维)。
初始:\(dp_{cur,0}=c_{cur}\)。
转移:\(dp_{cur,j}=dp_{cur,j}+dp_{nxt,j-1}\)。
令 \(f_{cur,j}\) 表示以 \(cur\) 为全局根节点时距它不超过 \(j\) 个单位的节点权值和。
初始:\(f_1=dp_1\)。
转移:\(f_{nxt,j}=dp_{nxt,j}+f_{cur,j-1}-dp_{nxt,j-2}\)。
(将 \(nxt\) 提起来时,子树内原贡献不变,还要加上子树外距 \(nxt \ j\) 个单位的节点,即距 \(cur \ j-1\) 个单位的节点。最后,这些节点中有可能有某些是在 \(nxt\) 子树内,要剔除它们的贡献。它们到 \(nxt\) 要走两遍边 \(cur \to nxt\),因此是 \(dp_{nxt,j-2}\))
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=31;
int n,k,c[N];
vector<int> G[N<<1];
int dp[N][M],f[N][M],ans[N];
void dfs1(int cur,int fa){
dp[cur][0]=c[cur];
for(int i:G[cur]){
if(i==fa) continue;
dfs1(i,cur);
for(int j=1;j<=k;j++)
dp[cur][j]+=dp[i][j-1];
}
}
void dfs2(int cur,int fa){
for(int i:G[cur]){
if(i==fa) continue;
for(int j=0;j<=k;j++)
f[i][j]=dp[i][j]+f[cur][j-1]-dp[i][j-2];
dfs2(i,cur);
}
}
int main(){
ios::sync_with_stdio(0);
cin>>n>>k;
for(int i=1,u,v;i<n;i++)
cin>>u>>v,
G[u].push_back(v),
G[v].push_back(u);
for(int i=1;i<=n;i++) cin>>c[i];
dfs1(1,0);
for(int i=0;i<=k;i++)
f[1][i]=dp[1][i];
dfs2(1,0);
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++)
ans[i]+=f[i][j];
cout<<ans[i]<<'\n';
}
return 0;
}
P3761
删除一条边后,一棵树变为了两棵树,显然连它们的中心最优。
于是答案变为:
于是我们枚举删哪条边,算出以上信息即可。
\(O(n^2)\)。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e3+5;
int n,ans=1e18,f[N];
int dis,dis1,dis2;
struct E{ int v,w; };
vector<E> T[N<<1];
int len1[N],len2[N],up[N];
bool vis[N];
int u[N],v[N],w[N];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
x=x*10+ch-'0',ch=getchar();
return x*f;
}
void dfsd(int cur,int fa){
vis[cur]=1;
for(auto i:T[cur]){
if(i.v==fa) continue;
dfsd(i.v,cur);
if(len1[i.v]+i.w>len1[cur])
len2[cur]=len1[cur],
len1[cur]=len1[i.v]+i.w;
else if(len1[i.v]+i.w>len2[cur])
len2[cur]=len1[i.v]+i.w;
}
dis=max(dis,len1[cur]+len2[cur]);
}
void dfsu(int cur,int fa){
for(auto i:T[cur]){
if(i.v==fa) continue;
up[i.v]=up[cur]+i.w;
if(len1[i.v]+i.w!=len1[cur])
up[i.v]=max(up[i.v],len1[cur]+i.w);
else
up[i.v]=max(up[i.v],len2[cur]+i.w);
dfsu(i.v,cur);
}
}
int fnd(int x){
return (f[x]==x?x:f[x]=fnd(f[x]));
}
void uni(int x,int y){
x=fnd(x),y=fnd(y);
if(x!=y) f[x]=y;
}
signed main(){
//ios::sync_with_stdio(0);
n=read();
for(int i=1;i<n;i++)
u[i]=read(),v[i]=read(),w[i]=read();
for(int i=1;i<n;i++){
for(int j=1;j<=n;j++) f[j]=j;
for(int j=1;j<=n;j++) T[j].clear();
for(int j=1;j<=n;j++) len1[j]=len2[j]=up[j]=0;
dis1=dis2=0;
for(int j=1;j<n;j++)
if(j!=i)
uni(u[j],v[j]),
T[u[j]].push_back({v[j],w[j]}),
T[v[j]].push_back({u[j],w[j]});
dis=0,dfsd(u[i],0);
dis1=dis;
dis=0,dfsd(v[i],0);
dis2=dis;
dfsu(u[i],0),dfsu(v[i],0);
int minx=1e18,miny=1e18;
for(int j=1;j<=n;j++){
if(fnd(j)==fnd(u[i])&&minx>max(len1[j],up[j]))
minx=max(len1[j],up[j]);
if(fnd(j)==fnd(v[i])&&miny>max(len1[j],up[j]))
miny=max(len1[j],up[j]);
}
ans=min(ans,max({dis1,dis2,w[i]+minx+miny}));
}
cout<<ans;
return 0;
}