Living-Dream 系列笔记 第73期

Posted on 2024-08-04 16:31  _XOFqwq  阅读(1)  评论(0编辑  收藏  举报

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\) 进行转移即可:

\[dp_{cur}=\max(dp_{cur},dp_{p}+1) \]

\(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

删除一条边后,一棵树变为了两棵树,显然连它们的中心最优。

于是答案变为:

\[\min(树 \ 1 \ 的直径,树 \ 2 \ 的直径,树 \ 1 \ 的最长链 + 树 \ 1 \ 的最长链 + 删除的那条边的边权) \]

于是我们枚举删哪条边,算出以上信息即可。

\(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;
}