BZOJ2067: [Poi2004]SZN 树形DP

来源:互联网 发布:中科院的飞箭软件 编辑:程序博客网 时间:2024/06/05 06:13

题目大意:http://www.lydsy.com/JudgeOnline/problem.php?id=2067

这个题一共两问,先求第一问:也就是问这个图最少需要几笔画出来, ans1=(奇点个数+1)/2

然后第二问就是一个树形DP,f[i]表示以i的子树下面最少需要补一个多长的链,如何转移f[i]呢?

先二分答案,设答案为Lim

把所有f[son[i]]扔到一个set里,然后在set里从后往前扫,把每一个最大的元素通过--upper_bound来找到和他配对里最大的,如果找不到配对就自己单成一条链,然后把这样单独成链里的长度取个最小值,f[i]=最小值+1。

如果没有任何一个需要单独成链的话,f[i]=1

然后注意如果f[i]>Lim,就需要给f[i]-Lim,链的总数++

最后链的总数如果=ans1,那么继续向下二分,否则向上二分

#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<set>using namespace std;const int N=10005;const int M=20005;const int inf=1e9;int n,cnt,to[M],nxt[M],lj[N],du[N],ans1,ans2,f[N],fa[N],t[N],t1[N],sum;void ins(int f,int t) {cnt++,to[cnt]=t,nxt[cnt]=lj[f],lj[f]=cnt,du[f]++;}void add(int f,int t) {ins(f,t),ins(t,f);}void dfs(int x,int lim){multiset<int>S;f[x]=inf;for(int i=lj[x];i;i=nxt[i])if(to[i]!=fa[x]){fa[to[i]]=x;dfs(to[i],lim);S.insert(f[to[i]]);}multiset<int>::iterator it=S.end(),itt;if(it!=S.begin()){it--;while(1){int tmp=*it;S.erase(it);sum++;itt=S.upper_bound(lim-tmp);if(itt!=S.begin()){itt--;S.erase(itt);}else f[x]=min(f[x],tmp);if(S.size()==0) break;else it=--S.end();}}if(f[x]==inf) f[x]=0;else sum--;f[x]++;if(f[x]>lim) f[x]-=lim,sum++;}bool Judge(int x){memset(f,0,sizeof(f));sum=0;dfs(1,x);if(f[1]>1) sum++;return sum==ans1;}int main(){scanf("%d",&n);int x,y;for(int i=1;i<n;i++){scanf("%d%d",&x,&y);add(x,y);}for(int i=1;i<=n;i++) ans1+=(du[i]%2);ans1=(ans1+1)/2;printf("%d ",ans1);int l=1,r=n-1,ans=0;while(l<=r){int mid=(l+r)>>1;if(Judge(mid)) ans2=mid,r=mid-1;else l=mid+1;}printf("%d\n",ans2);}


0 0
原创粉丝点击