P7920-[Kubic]Permutation

1|0正题

题目链接:https://www.luogu.com.cn/problem/P7920


1|1题目大意

一个排列p生成的森林的形式如下,对于每个i找到最大的j[1,i)满足 pi>pj,然后连一条i,j之间的边。
给出一张树G,求一个字典序最大的排列p使得生成的树与G同构。

1n5000


1|2解题思路

先考虑暴力的方法,我们可以枚举一个根,然后开始肯定根是1,然后子节点中假设大小都不相同我们肯定是从小到大放,因为假设现在放到k,那么每棵子树的根放的肯定是ksiz+1(因为一个根要是子树中最小的)。如果有大小相同的我们可以比较每棵内部放好时的字典序然后从大到小放。

显然这样是三方的,我们需要优化。考虑到我们每次需要比较子树的字典序大小,而对于所有根来说整棵就只有2n2棵子树(每条边的正反方向的子树),我们可以把这些子树都拿出来比较。

先按照子树大小排序,然后我们按照子树大小从小到大排同大小子树的顺序,因为排大的子树时需要用到小子树的顺序关系。

而且不难观察到最优的根一定是与某个叶子连接的点,同样的这个叶子它的另一边是一个大小为n1的子树,所以我们拿顺序最小的大小为n1的子树出来输出即可。

时间复杂度:O(n2logn),但是由于子树的字典序排序只会出现在同大小之间,所以常数很小,可以通过本题。


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<cctype> using namespace std; const int N=1e4+10; struct node{ int id,siz; vector<int> v; }p[N]; struct edge{ int to,next,from; }a[N]; int n,tot,ls[N],g[N],f[N],siz[N]; int read() { int x=0,f=1; char c=getchar(); while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();} while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar(); return x*f; } bool cmp(int x,int y) {return f[x]<f[y];} bool cMp(node x,node y) {return x.siz<y.siz;} bool CmP(node x,node y){ int l=min(x.v.size(),y.v.size()); for(int i=0;i<l;i++) if(f[x.v[i]]!=f[y.v[i]]) return f[x.v[i]]<f[y.v[i]]; return 0; } void addl(int x,int y){ a[++tot].to=y; a[tot].from=x; 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]; } return; } void print(int x,int k){ for(int i=0;i<p[x].v.size();i++){ printf("%d ",k-p[g[p[x].v[i]]].siz+1); print(g[p[x].v[i]],k); k-=p[g[p[x].v[i]]].siz; } return; } int main() { n=read(); if(n==1)return puts("1")&0; for(int i=1;i<n;i++){ int x=read(),y=read(); addl(x,y);addl(y,x); } dfs(1,0); for(int z=1;z<=tot;z++){ int x=a[z].from,y=a[z].to; p[z].siz=(siz[y]>siz[x])?(n-siz[x]):siz[y]; for(int i=ls[y];i;i=a[i].next){ int w=a[i].to; if(w==x)continue; p[z].v.push_back(i); } p[z].id=z; } sort(p+1,p+1+tot,cMp); int l=1,r=0,cnt=0; for(int S=2;S<n;S++){ while(l<=tot&&p[l].siz<S)l++; while(r<tot&&p[r+1].siz<=S)r++; for(int x=l;x<=r;x++) sort(p[x].v.begin(),p[x].v.end(),cmp); sort(p+l,p+r+1,CmP); f[p[l].id]=++cnt; if(S==n-1)break; for(int x=l+1;x<=r;x++){ cnt+=CmP(p[x-1],p[x]); f[p[x].id]=cnt; } } for(int i=1;i<=tot;i++)g[p[i].id]=i; printf("1 %d ",n); print(l,n-1); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15490978.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(78)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示