51nod-1322: 关于树的函数
【传送门:51nod-1322】
简要题意:
给出n个点的两棵无根树,编号都是从0到n-1
现在每棵树任意选出一条边割断,设第一棵树选出的边为e1,第二棵树选出的边为e2
很显然割断后两棵树各分成了四棵树,设第一棵树分成了A1树和B1树,第二棵树分成了A2树和B2树
设S(a,b)为a树和b树之间相同编号的点的个数
那么割断这两条边的价值为S(A1,B1),S(A1,B2),S(A2,B1),S(A2,B2)中的最大值的平方
求出每对e1,e2的价值和
题解:
又是一道卡了挺久的题
我们把节点全部+1,把第二棵树的节点编号设为n+1到2n(方便操作)
设两棵树的根节点分别为1和n+1
先在第一棵树中找到一个非根节点,然后在第二棵树中找到另一个非根节点
对于这两个节点显然有三种情况:
1.取两个节点的子树
2.取其中一个点的子树,另一个点的反子树(就是除了子树外的点)
3.取两个节点的反子树
然后对于三种情况更新答案就行了
具体操作有点麻烦,看代码吧(有点小懒)
PS:不知道该分到哪个专题,看了一下路牌,发现dalao都说是树形计数DP,那我就只好分成树形计数DP
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; struct node { int x,y,next; }a[21000];int len,last[8100]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int tot[8100],n; bool v[4100][4100]; void pre(int x,int fa) { tot[x]=1; if(x<=n) v[x][x]=true; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y==fa) continue; pre(y,x); if(x<=n) for(int i=1;i<=n;i++) v[x][i]|=v[y][i]; tot[x]+=tot[y]; } } LL ans;int s; int dou(int x){return x*x;} void getd(int x,int fa,int p) { if(v[p][x-n]==true) s++; int t=s; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y==fa) continue; getd(y,x,p); t=s-t; ans+=dou(max(max(t,tot[y]-t),max(tot[p]-t,n-tot[p]-tot[y]+t))); t=s; } } void solve(int x,int fa) { for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y==fa) continue; s=0;getd(n+1,0,y); solve(y,x); } } int main() { scanf("%d",&n); len=0;memset(last,0,sizeof(last)); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y);x++;y++; ins(x,y);ins(y,x); } for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y);x++;y++; ins(x+n,y+n);ins(y+n,x+n); } memset(v,false,sizeof(v)); pre(1,0);pre(n+1,0); ans=0;solve(1,0); printf("%lld\n",ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚