正题
题目链接:https://ac.nowcoder.com/acm/contest/20107/C
题目大意
给出nnn个点的一棵树,求一个最长的序列使得数字互不相同且相邻编号节点的都是祖孙关系。
1≤n≤105,1≤T≤51\leq n\leq 10^5,1\leq T\leq 51≤n≤105,1≤T≤5
解题思路
wx,iw_{x,i}wx,i表示节点xxx第iii次下来最多能获得的代价(保证前面最优的情况),那么转移的时候我们之间把子节点的www从大到小排序然后因为节点xxx有一次额外机会向下,所以我们还要把前两个最大的www给合并。
这个过程用启发式合并+堆就好了,时间复杂度:O(nlogn)O(n\log n)O(nlogn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+10;
struct node{int to,next;
}a[N<<1];
int T,n,tot,ls[N],siz[N],son[N];
priority_queue<int> s[N],q;
void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
void dfs(int x,int fa){siz[x]=1;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;dfs(y,x);siz[x]+=siz[y];if(siz[y]>siz[son[x]])son[x]=y;}return;
}
void solve(int x,int fa,int top){for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa||y==son[x])continue;solve(y,x,y);}if(son[x])solve(son[x],x,top);for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa||y==son[x])continue;while(!s[y].empty())q.push(s[y].top()),s[y].pop();}int w=0;if(!q.empty())w+=q.top(),q.pop();if(!q.empty())w+=q.top(),q.pop();q.push(w+1);if(x==top)while(!q.empty())s[x].push(q.top()),q.pop();return;
}
int main()
{scanf("%d",&T);while(T--){memset(ls,0,sizeof(ls));memset(son,0,sizeof(son));scanf("%d",&n);tot=0;for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);}dfs(1,1);solve(1,1,1);printf("%d\n",s[1].top());while(!s[1].empty())s[1].pop();}return 0;
}