2024.4.12 树状结构补题
P2015 二叉苹果树
\(f_{u,i}\) 表示 u 的子树内保留了 i 条边的最大贡献。
\(f_{u,i}=max{f_{v,j}+f{u,i-j-1}+w}\)
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 210
#define maxm 300000
#define INF 0x3f3f3f3f
//#define int long long
using namespace std;
bool vis[maxn];
int n,m,s,t,tot=1,res,ans;
int f[maxn][maxn],head[maxn],siz[maxn];
struct edge{int fr,to,dis,nxt;}e[maxm];
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
void add(int fr,int to,int dis){
e[++tot].fr=fr;e[tot].to=to;
e[tot].nxt=head[fr];e[tot].dis=dis;
head[fr]=tot;
}
void dfs(int u,int fa){
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].to;
if(to==fa) continue;
dfs(to,u);siz[u]+=siz[to]+1;
for(int j=min(siz[u],m);j>=1;j--)
for(int k=min(siz[to],j-1);k>=0;k--)
f[u][j]=max(f[u][j],f[u][j-k-1]+f[to][k]+e[i].dis);
}
}
int main(){
n=read();m=read();
for(int i=1,fr,to,dis;i<n;i++){
fr=read();to=read();dis=read();
add(fr,to,dis);add(to,fr,dis);
}
dfs(1,0);
printf("%d\n",f[1][m]);
return 0;
}
P4281 [AHOI2008] 紧急集合 / 聚会
两两求 lca,会发现只有两种
然后手玩会发现非共享 lca 的贡献更小
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 2100000
#define maxm 100100
#define INF 0x3f3f3f3f
#define int long long
using namespace std;
int val[maxn];
int n,m,tot,root;
int dep[maxn],son[maxn],fa[maxn];
int top[maxn],siz[maxn],head[maxn];
struct edge{int fr,to,nxt;}e[maxn];
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
void add(int fr,int to){
e[++tot].fr=fr;e[tot].to=to;
e[tot].nxt=head[fr];
head[fr]=tot;
}
void dfs1(int u,int fat){
fa[u]=fat;dep[u]=dep[fat]+1;siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].to;
if(to==fat) continue;
dfs1(to,u);siz[u]+=siz[to];
if(siz[to]>siz[son[u]]) son[u]=to;
}
}
void dfs2(int u,int tp){
top[u]=tp;
if(son[u]) dfs2(son[u],tp);
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].to;
if(to==fa[u]||to==son[u]) continue;
dfs2(to,to);
}
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
signed main(){
n=read();m=read();
for(int i=1,fr,to;i<n;i++){
fr=read();to=read();
add(fr,to);add(to,fr);
}
dfs1(1,0);dfs2(1,1);
for(int i=1,x,y,z;i<=m;i++){
x=read();y=read();z=read();
if(lca(x,y)==lca(y,z)&&lca(x,y)==lca(x,z)){
int Lca=lca(x,y);
printf("%lld %lld\n",Lca,dep[x]+dep[y]+dep[z]-3*dep[Lca]);
}
else{
int Lcaxy=lca(x,y),Lcayz=lca(y,z),Lcaxz=lca(x,z),now=-1,id;
now=max(dep[Lcaxy],max(dep[Lcayz],max(dep[Lcaxz],now)));
if(now==dep[Lcaxy]) id=Lcaxy;
else if(now==dep[Lcayz]) id=Lcayz;
else id=Lcaxz;
int LCAx=lca(id,x),LCAy=lca(id,y),LCAz=lca(id,z);
printf("%lld %lld\n",id,dep[x]+dep[y]+dep[z]+3*now-2*(dep[LCAx]+dep[LCAy]+dep[LCAz]));
}
}
return 0;
}
P3629 [APIO2010] 巡逻
k=1 时贪心连树的直径两端即可。
k=2 时先连树的直径,然后割除这个直径再贪心一次即可。
根据求树的直径的方法可以在第二次把直径上的边权改为负数。
#include<bits/stdc++.h>
#define maxn 100010
using namespace std;
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
int l1,l2,n,k,point,cnt,ans;
int f[maxn],d[maxn],v[maxn];
int dis[maxn],vis[maxn],head[maxn];
struct node{int to,w,next;}e[maxn];
void add(int a,int b,int c){
e[++cnt].to=b;
e[cnt].next=head[a];
head[a]=cnt;
e[cnt].w=1;
}
void bfs(int x,int ck){
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
queue<int >q;q.push(x);vis[x]=1;
while(!q.empty()){
int now=q.front();q.pop();
for(int i=head[now];i;i=e[i].next){
int y=e[i].to;
if(!vis[y]){
vis[y]=1;
dis[y]=dis[now]+e[i].w;
q.push(y);if(ck)f[y]=now;
}
}
}
for(int i=1;i<=n;i++){
if(ans<dis[i])ans=dis[i],point=i;
}
}
void change(int a){
while(f[a]){
int fa=f[a];
for(int i=head[fa];i;i=e[i].next)
if(e[i].to==a){e[i].w=-1;break;}
for(int i=head[a];i;i=e[i].next){
if(e[i].to==fa){e[i].w=-1;break;}
}
a=fa;
}
}
void dp(int x){
v[x]=1;
for(int i=head[x];i;i=e[i].next){
int y=e[i].to;
if(v[y])continue;dp(y);
ans=max(ans,dis[x]+dis[y]+e[i].w);
dis[x]=max(dis[x],dis[y]+e[i].w);
}
}
int main(){
n=read();k=read();
for(int i=1,a,b;i<n;i++){
a=read();b=read();
add(a,b,1);add(b,a,1);
}
if(k==1){
bfs(1,0);
bfs(point,0);
printf("%d",2*n-dis[point]-1);
}
else{
bfs(1,0);
bfs(point,1);
l1=dis[point];
change(point);
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
ans=0;
dp(1);
printf("%d",2*n-ans-l1);
}
return 0;
}