2024.4.12 樹狀結構補題

KnightL發表於2024-04-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;
}

相關文章