【POI2012】polarization(树的重心,dp优化,结论)
2018-01-30 19:43
323 查看
题目大意:
给你一颗树,对于每条边你可以自己选择方向,但是只能一个方向,如果点i,j可以互相到达,那么i,j就是匹配的。求出匹配数最小和最大的值。思路:
这题的匹配最小值很容易求,手玩几个图就可以发现为n-1,证明就是所有深度为偶数的向下走,为奇数的向上走,每条边就只会匹配一个点就不会延伸了。对于最大的匹配数就特别恶心了,而且这题还有没部分分,哎。首先我们要猜一个结论,结论就是最大匹配数的方案图像,里面有一个点他的个个子树里面的边方向相同。这样就可以枚举那个汇点在哪里,为了让他们的匹配树尽可能的大,那么向上的就要尽量接近n-1/2,所以要用dp来判断最接近n-1/2的数是多少。但是一般的dp是n^2的,加上前面的一个n就n^2超时了,这是就要用到一种神奇的dp优化,17年noip有讲,我这里就大概聊聊。你可以把点的子树数看成x,子数的节点数看成y,如果x>根号n那么y<根号n,你就开了一个根号n的多重背包,用优化写logn,如果x<根号n,那么y必然>根号n,这就是只有根号n个物品的背包问题了。
预处理一下子树权值,点数,子树方向相同的匹配数,然后每个点搞一搞就好了。
%%%%%%%%%
好像还有一个优化没有说,就是只要从重心开始做上面的讲法,不是重心好像就可以就可以直接o(子树数)出结了。
程序:
#include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #define N 500001 using namespace std; typedef long long LL; int last ,q ,hash ,size ; int cnt,n; LL dis ,disp ; struct data{int t,next;}e[N*2]; void add(int x,int y){ e[++cnt].t=y; e[cnt].next=last[x]; last[x]=cnt; e[++cnt].t=x; e[cnt].next=last[y]; last[y]=cnt; } LL could ,num ,f ,f1 ; long long work(int u){ int i,top=0; long long ans=0; for (int i=last[u];i;i=e[i].next){ int v=e[i].t; if (hash[v]==u){ ans+=dis[v]+size[v]; could[++top]=size[v]; } else { ans+=disp[u]; could[++top]=n-size[u]; } } for (int i=1;i<=top;i++) if (could[i]*2>=n-1) return ans+LL(n-1-could[i])*could[i]; int T=sqrt(n)+1; for (int i=1;i<=T;i++) num[i]=0; memset(f,0,sizeof(f)); f[0]=1; int half=(n+1)/2; for (int i=1;i<=top;i++){ if (could[i]<=T) num[could[i]]++; else for (int j=half;j>=could[i];--j) f[j]|=f[j-could[i]]; } for (int i=1;i<=T;i++){ if (!num[i]) continue; for (int j=0;j<=half;j++) f1[j]=0; for (int j=0;j<=i-1;j++){ int sum=0; for (int k=0;k*i+j<=half;++k){ sum+=f[k*i+j]; f1[k*i+j]=(sum>0); if (k>=num[i]) sum-=f[(k-num[i])*i+j]; } } for (int j=0;j<=half;j++) f[j]=f1[j]; } long long t=0; for (int i=0;i<=half;i++) if (f[i]) t=max(t,ans+i*LL(n-1-i)); return t; } int main(){ freopen("polarization.in","r",stdin); freopen("polarization.out","w",stdout); scanf("%d",&n); for (int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); } int h=1,t=1; q[t++]=1; hash[1]=-1; while (h<t){ int u=q[h++]; for (int i=last[u];i;i=e[i].next){ int v=e[i].t; if (!hash[v]){ q[t++]=v; hash[v]=u; } } } for (int i=n;i>=1;--i){ int u=q[i]; size[u]=1; for (int j=last[u];j;j=e[j].next){ int v=e[j].t; if (hash[v]!=u) continue; dis[u]+=dis[v]+size[v]; size[u]+=size[v]; } } disp[1]=0; for (int i=1;i<=n;i++){ int u=q[i]; for (int j=last[u];j;j=e[j].next){ int v=e[j].t; if (hash[v]!=u) continue; disp[v]=dis[u]+disp[u]-(dis[v]+size[v])+(n-size[v]); } } long long ans=0; for (int i=1;i<=n;i++) ans=max(ans,work(i)); e655 printf("%d %lld",n-1,ans); }
相关文章推荐
- 【JZOJ3626】【POI2012】polarization (贪心+树+背包+二进制优化)
- bzoj 2806 [Ctsc2012]Cheat 后缀自动机 单调队列优化dp
- BZOJ 2726: [SDOI2012]任务安排 [斜率优化DP 二分 提前计算代价]
- [后缀自动机][单调队列优化DP] BZOJ 2806: [Ctsc2012]Cheat
- JZOJ3626. 【POI2012】polarization
- BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP
- [BZOJ2806][Ctsc2012]Cheat(后缀自动机+单调队列优化dp)
- bzoj 2798 [Poi2012]Bidding 博弈论 dp
- [BZOJ2806][Ctsc2012][后缀自动机][队列优化][DP]Cheat
- bzoj2806 【Ctsc2012】 Cheat 后缀自动机+单调队列优化dp
- bzoj3831 [Poi2014]Little Bird 单调队列优化dp
- 【bzoj2806】[Ctsc2012]Cheat 广义后缀自动机+二分+单调队列优化dp
- bzoj2806 [Ctsc2012]Cheat(单调队列优化dp+二分+广义SAM)
- 【bzoj3831】[Poi2014]Little Bird(单调队列优化dp)
- [POI 2012]Cloakroom(DP)
- 【bzoj3831】[Poi2014]Little Bird 单调队列优化dp
- BZOJ 2806: [Ctsc2012]Cheat 后缀自动机+单调队列优化DP
- BZOJ 2794: [Poi2012]Cloakroom 询问离线 背包dp
- BZOJ 2806 [Ctsc2012]Cheat ——后缀自动机 单调队列优化DP
- 【BZOJ2803】[Poi2012]Prefixuffix 结论题