P7920 [Kubic] Permutation做题笔记
P7920 [Kubic] Permutation做题笔记
感觉这题很有趣!
题意是说:我们根据一个排列 \(p\) ,来构造一个树 \(T_p\) 。方法是对于 \(i\in(1,n]\) ,我们定义 \(S_i={x|x\in[1,i),p_x>p_i}\) ,然后定义 \(f_i=\max_{x\in S_i}x\) ,然后我们连一条 \(f_i\) 到 \(i\) 的无向边,最终形成的就是 \(T_p\) 。现在我们给定一棵树 \(T\) ,要求你找到一个排列 \(q\) 满足 \(T_q\) 与 \(T\) 同构,并且在满足上一个要求的排列中其字典序最大。 \(n\le 5\times 10^3\) 。
我们先来思考不要求字典序最大时应该怎么做。首先我们构造答案排列 \(ans=\{n,n-1,n-2...1\}\) ,然后我们首先要把 \(1\) 放到最前面来,因为他不会向任何点连边,所以我们将 \([1,n]\) 这个区间整体右移一下,形成的排列是 \(\{1,n,n-1,n-2...2\}\) 。然后我们考虑根的第一棵子树,设他的大小是 \(s\) ,那么因为别的点不能向这个子树内连边,所以这棵子树内的点在排列中的下标一定是 \(\{n,n-1,n-2...n-s+1\}\) ,在这个区间内,我们第一个数也应当是这个区间内最小的数,也就是把这个区间整体右移一下。于是我们便可以按照这个方式递归构造。
下面再来考虑在字典序最大的限制下我们该怎么做。根据我们的构造方式,显然如果目前的 \(siz\) 越小,那么这一位就越大。所以我们按照 \(dfs\) 序把每棵子树的 \(siz\) 写下来,我们要求这个序列的字典序最小。做法是我们枚举每个点作为根,我们求出对于每个点作为根可以形成的字典序最小的序列是什么,然后对这 \(n\) 个序列求出字典序最小的那个,只需要从前向后枚举每一位即可。
于是我们的问题转化为了如何求出一个点作为根的时候形成的字典序最小的序列。显然的想法时我们可以把儿子们按照 \(siz\) 排序,但这样无法处理 \(siz\) 相同的情况。所以我们考虑从下往上考虑,假设我们已经知道了每个儿子的子树的答案,我们 \(sort\) 所用的 \(cmp\) 就是直接比较两个儿子的序列的字典序,下面代码里因为伞兵了所以做法是暴力 \(dfs\) 两棵子树,本质一样。虽然这样做一次的复杂度上限是 \(O(n^2\log n)\) ,但是极度的松,因为子树完全同构实在是很难。
如果我们暴力去枚举每个点作为根时的答案,那么即使我们认为那个 \(cmp\) 是 \(O(1)\) 的,复杂度也依然是可观的 \(O(n^2\log n)\) ,无法通过。做法是我们考虑换根 \(dp\) 的思想,事实上对于一个点 \(u\) ,我们可以提前预处理出所有与 \(u\) 相连的点的排序结果。那么我们如果以 \(1\) 为根,预处理出 \(u\) 的儿子排序结果,那么父亲该如何插入呢?回忆我们的 \(cmp\) ,第一步比较一定是先比较最初两棵的 \(siz\) ,那么我们只需要强制让父亲的 \(siz\) 大于所有儿子的 \(siz\) ,父亲就一定在排序结果的最后面了。那么我们如何做到这个“强制”呢?我们可以考虑以重心为根,根据重心的结论,一定有自己子树的 \(siz\) 不超过 \(\frac{n}{2}\) ,那么父亲的子树一定大于自己的每个儿子。
然后就做完了,复杂度 \(O(n^2\log n)\) ,因为很松所以可以随便过。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pb(x) push_back(x)
#define MAXN 5010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
const bool is_print=0;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
int head[MAXN],ednum;
struct edge{
int nxt,to;
}ed[MAXN*2];
void add_Edge(int u,int v)
{
ednum++;
ed[ednum].nxt=head[u],ed[ednum].to=v;
head[u]=ednum;
}
int n,rt,mxsiz=INF,siz[MAXN],son[MAXN][MAXN],N[MAXN],p[MAXN][MAXN],re[MAXN],dad[MAXN];
bool ok[MAXN];
void dfs1(int u,int fa)
{
siz[u]=1;
int tmp=0;
FED(i,u)
{
int v=ed[i].to;
if(v==fa) continue;
dfs1(v,u);
siz[u]+=siz[v],tmp=max(tmp,siz[v]);
}
tmp=max(tmp,n-siz[u]);
if(tmp<mxsiz) mxsiz=tmp,rt=u;
}
int dfs3(int u,int v)
{
if(siz[u]<siz[v]) return 1;
if(siz[u]>siz[v]) return -1;
FUP(i,1,min(N[u],N[v]))
{
int w=dfs3(son[u][i],son[v][i]);
if(w!=0) return w;
}
return 0;
}
bool cmp(int x,int y){return dfs3(x,y)>0;}
void dfs2(int u,int fa)
{
siz[u]=1,dad[u]=fa;
FED(i,u)
{
int v=ed[i].to;
if(v==fa) continue;
dfs2(v,u);
siz[u]+=siz[v];
son[u][++N[u]]=v;
}
if(is_print)
{
printf("u=%d siz=%d son:\n",u,siz[u]);
FUP(i,1,N[u])
{
printf("son=%d siz=%d\n",son[u][i],siz[son[u][i]]);
FUP(j,1,N[son[u][i]]) printf("%d ",son[son[u][i]][j]);
puts("");
}
}
sort(son[u]+1,son[u]+N[u]+1,cmp);
}
void dfs4(int u,int fa,int id,int &cur,int tot)
{
p[id][++cur]=tot;
FUP(i,1,N[u])
{
int v=son[u][i];
if(v==fa) continue;
if(v!=dad[u]) dfs4(v,u,id,cur,siz[v]);
else dfs4(v,u,id,cur,n-siz[u]);
}
}
void dfs5(int u,int fa,int &cur,int tot)
{
int tmp=re[cur+tot];
FDW(i,cur+tot,cur+2) re[i]=re[i-1];
re[++cur]=tmp;
FUP(i,1,N[u])
{
int v=son[u][i];
if(v==fa) continue;
if(v!=dad[u]) dfs5(v,u,cur,siz[v]);
else dfs5(v,u,cur,n-siz[u]);
}
}
int main(){
//freopen("data.in","r",stdin);
//freopen("zj.out","w",stdout);
n=read();
FUP(i,1,n-1)
{
int u=read(),v=read();
add_Edge(u,v),add_Edge(v,u);
}
dfs1(1,0);
if(is_print) printf("rt=%d\n",rt);
dfs2(rt,0);
FUP(i,1,n) if(i!=rt) son[i][++N[i]]=dad[i];
int tmp=0;
FUP(i,1,n) tmp=0,dfs4(i,0,i,tmp,n);
if(is_print)
{
FUP(i,1,n)
{
FUP(j,1,n) printf("%d ",p[i][j]);
puts("");
}
}
FUP(j,1,n)
{
re[j]=n+1-j;
int mn=INF;
FUP(i,1,n)
{
if(ok[i]) continue;
mn=min(mn,p[i][j]);
}
FUP(i,1,n) if(mn!=p[i][j]) ok[i]=true;
}
tmp=0;
FUP(i,1,n) if(!ok[i]){dfs5(i,0,tmp,n);break;}
FUP(i,1,n) printf("%d ",re[i]);
return 0;
}