您的位置:首页 > 其它

【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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: