CSP2025 - 树形 DP
CSP2025 - 树形 DP
T1 「MXOI Round 1」城市
这个 “树上两点距离之和” 很典,让我们想到换根 DP。
首先求出
接下来求出整棵树的所有点到
然后考虑第
总体复杂度
int siz[MAXN],d[MAXN],ans;
void dfs(int u,int fno){
siz[u]=1;
for(int i=head[u];i;i=e[i].to){
if(e[i].v==fno) continue;
dfs(e[i].v,u);
siz[u]+=siz[e[i].v];
add(d[u],(d[e[i].v]+1ll*siz[e[i].v]*e[i].w%MOD)%MOD);
}
}
void dfs2(int u,int fno){
for(int i=head[u];i;i=e[i].to)
if(e[i].v^fno){
d[e[i].v]=(d[u]+(n-2ll*siz[e[i].v]%MOD+MOD)%MOD*e[i].w%MOD)%MOD;
dfs2(e[i].v,u);
}
}
int main(){
n=read(),q=read();
for(int i=1,u,v,w;i<n;++i){
u=read(),v=read(),w=read();
addedge(u,v,w);
addedge(v,u,w);
}
dfs(1,0);
dfs2(1,0);
for(int i=1;i<=n;++i) add(ans,d[i]);
while(q--){
int k=read(),w=read();
write((ans+2ll*d[k]+2ll*n*w)%MOD);
}
return fw,0;
}
T2 [CF1285D] Dr. Evil Underscores
其实这题就是个贪心。首先异或
就没了。
int n,tot=1;
int trie[MAXN][2];
void insert(int x){
int p=1;
for(int i=30;~i;--i){
int c=(x>>i)&1;
if(!trie[p][c]) trie[p][c]=++tot;
p=trie[p][c];
}
}
int ask(int bit,int p){
if(bit<0) return 0;
if(trie[p][0]&&trie[p][1]) return min(ask(bit-1,trie[p][0]),ask(bit-1,trie[p][1]))+(1<<bit);
if(trie[p][0]) return ask(bit-1,trie[p][0]);
if(trie[p][1]) return ask(bit-1,trie[p][1]);
return 0;
}
int main(){
n=read();
for(int i=1;i<=n;++i) insert(read());
printf("%d\n",ask(30,1));
return 0;
}
T3 P1270 “访问”美术馆
感觉这题的读入难度大于 DP 难度
首先建边的时候就把边权建成
在叶子节点时,初始化边界条件
注意如果转移的时候
#include<bits/stdc++.h>
using namespace std;
constexpr int MAXN=6005;
int n,tot=2,son[MAXN],siz[MAXN],f[MAXN][MAXN];
vector<pair<int,int>>g[MAXN];
void init(int u,int fno){
int w,v;
scanf("%d%d",&w,&v);
g[fno].emplace_back(u,w<<1);
if(v) return son[u]=v,void();
init(++tot,u);
init(++tot,u);
}
void dfs(int u){
if(son[u]){
siz[u]=son[u]*5;
for(int i=5;i<=n;++i) f[u][i]=min(son[u],f[u][i-5]+1);
return;
}
for(auto vv:g[u]){
int v=vv.first,w=vv.second;
dfs(v);
siz[u]+=siz[v]+w;
for(int i=min(n,siz[u]);i>=w;--i)
for(int j=w;j<=min(i,siz[v]+w);++j)
f[u][i]=max(f[u][i],f[u][i-j]+f[v][j-w]);
}
}
int main(){
scanf("%d",&n);
init(2,1);
dfs(1);
printf("%d\n",f[1][n-1]);
return 0;
}
T4 [HAOI2015] 树上染色
将 “黑点两两距离” 和 “白点两两距离” 转化为针对边计算贡献,也就是对于每一条边所经过的次数,乘上边权再加起来就是总的答案。
而每一条边被经过的次数等于边的两侧同色点的个数的乘积。于是一条边被经过的次数就是:
其中
DP 方程又是很熟悉的树上背包。设
树上背包需要通过
ll siz[MAXN],f[MAXN][MAXN];
void dfs(int u,int fno){
siz[u]=1;
f[u][0]=f[u][1]=0;
for(int i=head[u],v,w;i;i=e[i].to){
if((w=e[i].w,v=e[i].v)==fno) continue;
dfs(v,u);
siz[u]+=siz[v];
for(int j=min(1ll*m,siz[u]);~j;--j){
if(~f[u][j]) f[u][j]+=f[v][0]+siz[v]*(n-m-siz[v])*w;
for(int k=min(1ll*j,siz[v]);k;--k)
if(~f[u][j-k]){
ll tot=1ll*k*(m-k)+(siz[v]-k)*(n-m-siz[v]+k);
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+tot*w);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
if(n-m<m) m=n-m;
for(int i=1,u,v,w;i<n;++i){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w),addedge(v,u,w);
}
memset(f,-1,sizeof(f));
dfs(1,0);
printf("%lld\n",f[1][m]);
return 0;
}
T9 P5007 DDOSvoid 的疑惑
手画一下发现一定是同父亲的两个子树之间会产生贡献,这个贡献的产生方式为:在这些子树中任选一些,把这些子树的权值加起来就是贡献。
于是,我们需要记录两个值:
然后考虑
至于这里为什么要分别算两遍
最后还需要给
ll f[MAXN],g[MAXN];
void dfs(int u,int fno){
for(int i=head[u],v;i;i=e[i].to){
if((v=e[i].v)==fno) continue;
dfs(v,u);
add(f[u],f[u]*g[v]%MOD+f[v]*g[u]%MOD+f[v]);
add(g[u],g[u]*g[v]%MOD+g[v]);
}
add(f[u],T?u:1);
++g[u];
}
int main(){
n=read(),T=read();
for(int i=1,u,v;i<n;++i){
u=read(),v=read();
addedge(u,v),addedge(v,u);
}
dfs(1,0);
printf("%lld\n",f[1]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现