ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树形DP

2021-04-09 13:02:13  阅读:224  来源: 互联网

标签:nxt cnt sz int fa 树形 DP he


树形DP

解决符合子树最优结构,或者链上问题(枚举链的最高点)等等

例题1

Luogu P3576 [POI2014]MRO-Ant colony

蚂蚁没有办法走回头路,且行动是可以预知的,只需要考虑经过食蚁兽所在的边,所以不妨倒着做,从食蚁兽出发得出每个可能的区间,然后排序后二分即可

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=1e6+5;
int cnt,to[N<<1],nxt[N<<1],he[N],R[N],L[N],son[N],a[N],n,m,k;
ll ans;

inline void add(int u,int v) {
	to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
}

inline void dp(int u,int siz,int l,int r) {
	if((ll)siz*l>a[m]) return;
	L[u]=siz*l;
	if((ll)siz*(r+1)-1>a[m]) R[u]=a[m];
		else R[u]=siz*(r+1)-1;
}

void dfs(int fa,int u) {
	for(int e=he[u];e;e=nxt[e]) {
		int v=to[e];
		if(v!=fa) {
			dp(v,(son[v]==0?1:son[v]),L[u],R[u]);
			if(L[v]!=0||R[v]!=0) {
				dfs(u,v);
			} 
		}
	}
}


inline int left(int x) {
	int l=1,r=m,ans;
	while(l<=r) {
		int mid=l+r>>1;
		if(a[mid]>=x) ans=mid,r=mid-1;
			else l=mid+1;  
	}
	return ans;
}

inline int right(int x) {
	int l=1,r=m,ans=0;
	while(l<=r) {
		int mid=l+r>>1;
		if(a[mid]<=x) ans=mid,l=mid+1;
			else r=mid-1;
	}
	return ans;
}

int main() {
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++) {
		scanf("%d",&a[i]);
	}
	sort(a+1,a+m+1);
	int rt=0,Fa=0;
	for(int i=1;i<n;i++) {
		int u,v; scanf("%d%d",&u,&v);
		add(u,v),add(v,u);
		son[u]++,son[v]++;
		if(i==1) {
			rt=u,Fa=v;
		}
	}
	for(int i=1;i<=n;i++) son[i]--;
	dp(rt,(son[rt]==0?1:son[rt]),k,k);
	dp(Fa,(son[Fa]==0?1:son[Fa]),k,k);
	dfs(Fa,rt),dfs(rt,Fa);
	for(int i=1;i<=n;i++) {
	//	if(!son[i]) printf("%d %d\n",L[i],R[i]);
		if(!son[i]&&(L[i]||R[i])) {
			ans+=right(R[i])-left(L[i])+1;
		}
	}
	printf("%lld\n",ans*k);
	return 0;
} 

例题2

Luogu P3237 [HNOI2014]米特运输

显然,如果一个点权值确定,则整棵树都确定了且是唯一的,不妨用根节点的权值表示树的权值

所以对于每个点权求出其对应的根节点的权值,用\(log\)改为加法,排序判断最小修改次数

#include<bits/stdc++.h> 
#define db double
const db eps=1e-7;
using namespace std;

const int N=1e6+5;
int n,cnt,to[N],nxt[N],he[N],a[N];
db f[N];

inline void add(int u,int v) {
    to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
}

void dfs(int fa,int u) {
    int son=0;
    for(int e=he[u];e;e=nxt[e]) {
        int v=to[e];
        if(v!=fa){
            dfs(u,v);
            son++;
        }
    }
    if(son) f[u]=log(son);
}

void dgs(int fa,int u) {
    f[u]+=f[fa];
    for(int e=he[u];e;e=nxt[e]) {
        int v=to[e];
        if(v!=fa) {
            dgs(u,v);
        }
    }
    f[u]=f[fa]+log(a[u]);
}

int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<n;i++) {
        int u,v; scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs(0,1); dgs(0,1);
    sort(f+1,f+n+1);
    int ans=n,now=1;
    for(int i=2;i<=n;i++) {
        if(abs(f[i]-f[i-1])<=eps) now++;
            else {
                ans=min(ans,n-now);
                now=1;
            }
    }
    ans=min(ans,n-now);
    printf("%d\n",ans);
    return 0;
}

例题3

Luogu P4099 [HEOI2013]SAO

有向图求拓扑序种类是NP问题

设\(f[i]\)表示\(i\)子树的拓扑序的种类,显然是无法转移的

所以再记一维表示根节点的拓扑序

对于\((u,v)\),

  1. 若\(u\)先,则

    \[f[u][k]=\sum_{i=1}^{k}f[u][i]\times C_{k-1}^{i-1}\times C_{sz[u]+sz[v]-k}^{sz[u]-i}\times f[v][j],j\in(k-i,sz[v]] \]

  2. 若v先,则

    \[f[u][k]=\sum_{k=1}^{k}f[u][i]\times C_{k-1}^{i-1}\times C_{sz[u]+sz[v]-k}^{sz[u]-i}\times f[v][j],j\in[1,k-i] \]

前缀和优化即可

#include<bits/stdc++.h>
#define ll long long
const int p=1e9+7;
using namespace std;
inline int mo(int x) {
    return x>=p?x-p:x;
}

const int N=1005;
int cnt,n,to[N<<1],nxt[N<<1],he[N],w[N<<1];
ll f[N][N];
int siz[N],c[N][N];
char op[10];

inline void add(int u,int v,int k) {
    to[++cnt]=v,nxt[cnt]=he[u],w[cnt]=k;
    he[u]=cnt;
}

void dfs(int fa,int u) {
    siz[u]=1; f[u][1]=1;
    for(int e=he[u];e;e=nxt[e]) {
        int v=to[e];
        if(v!=fa) {
            dfs(u,v);
            if(!w[e]) {
                for(int k=siz[u]+siz[v];k;k--) {
                    int sum=0;
                    for(int i=1;i<=min(siz[u],k);i++) {
                        ll t=mo(f[v][siz[v]]-f[v][k-i]+p);
                        sum=mo(sum+t*f[u][i]%p*c[i-1][k-1]%p*c[siz[u]-i][siz[u]+siz[v]-k]%p);
                    }
                    f[u][k]=sum;
                }
            } else {
                for(int k=siz[u]+siz[v];k;k--) {
                    int sum=0;
                    for(int i=1;i<=min(siz[u],k);i++) {
                        ll t=f[v][min(siz[v],k-i)];
                        sum=mo(sum+t*f[u][i]%p*c[i-1][k-1]%p*c[siz[u]-i][siz[u]+siz[v]-k]%p);
                    }
                    f[u][k]=sum;
                }
            }
            siz[u]+=siz[v];
        }
    }
    for(int i=2;i<=siz[u];i++) {
        f[u][i]=mo(f[u][i]+f[u][i-1]);
    }
}
int main() {
    int T; scanf("%d",&T);
    c[0][0]=1;
    for(int j=1;j<=1000;j++) {
        c[0][j]=1;
        for(int i=1;i<=j;i++) {
            c[i][j]=mo(c[i][j-1]+c[i-1][j-1]);
        }
    }
    while(T--) {
        scanf("%d",&n);
        for(int i=1;i<n;i++) {
            int u,v;
            scanf("%d%s%d",&u,op,&v);
            ++u,++v;
            add(u,v,op[0]=='>'),add(v,u,op[0]=='<');
        }
        dfs(0,1);
        printf("%d\n",f[1][n]);
        cnt=0;
        for(int i=1;i<=n;i++) he[i]=0;
        memset(f,0,sizeof(f));
        memset(siz,0,sizeof(siz));
    }
    return 0;
}

树形DP+贪心(扰动法)

Luogu P3574 [POI2014]FAR-FarmCraft

显然可以树形DP,\(f[i]\)表示\(i\)的子树中的最大装配时间,\(g[i]\)表示\(i\)的赶路时间

考虑如何转移\(f\),貌似要\(O(n!)\)

然后利用扰动法得出:(交换相邻两项使之变劣)

\[f[v1]-g[v1]>=f[v2]-g[v2] \]

#include<bits/stdc++.h>
using namespace std;

const int N=5e5+5;
int cnt,to[N<<1],nxt[N<<1],he[N],n,c[N],f[N],g[N];

struct A{ int u,s; };
bool operator <(A i,A j) {
    return i.s<j.s;
}

priority_queue<A>q;

inline void add(int u,int v) {
    to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
}

void dfs(int fa,int u) {
    for(int e=he[u];e;e=nxt[e]) {
        int v=to[e];
        if(v!=fa) dfs(u,v);
    }
    for(int e=he[u];e;e=nxt[e]) {
        int v=to[e];
        if(v!=fa) q.push((A){v,f[v]-g[v]});    
    }
    f[u]=c[u];
    while(!q.empty()) {
        int v=q.top().u; q.pop();
        f[u]=max(f[u],f[v]+g[u]+1);
        g[u]+=g[v]+2;
    }
}

int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&c[i]);
    }
    for(int i=1;i<n;i++) {
        int u,v; scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs(0,1);
    printf("%d\n",max(f[1],c[1]+g[1]));
    return 0;
}

标签:nxt,cnt,sz,int,fa,树形,DP,he
来源: https://www.cnblogs.com/wsfwsf/p/14636638.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有