Time Limits: 2000 ms
Memory Limits: 262144 KB
输出到文件 tree.out 中。
对于每组测试数据,输出一行共 n 个用空格隔开的整数,表示最优操作方案下所能得到的字典序最小的 Pi。
4
5
2 1 3 5 4
1 3
1 4
2 4
4 5
5
3 4 2 1 5
1 2
2 3
3 4
4 5
5
1 2 5 3 4
1 2
1 3
1 4
1 5
10
1 2 3 4 5 7 8 9 10 6
1 2
1 3
1 4
1 5
5 6
6 7
7 8
8 9
9 10
1 3 4 2 5
1 3 5 2 4
2 3 1 4 5
2 3 4 5 6 1 7 8 9 10
这道题目质量非常高。虽然它的算法并不难,要想出来却十分有难度;即便是想出来了,也很难实现。就算你说考场上要求不高,能水点分就好了,也会发现它的链和菊花的情况十分恶心(读入也是极其恶心的,除了对正解方便之外)。
这题很显然不是DP或递推什么的东西(你把状态给我表示出来试试),因此考虑贪心。
最直接的想法就是每次从1到n枚举一个数字,然后找出它能交换到哪一个编号最小的节点。这个东西可以 n 2 n^2 n2来弄,总的时间复杂度就是 O ( n 3 ) O(n^3) O(n3)的了。
考虑怎么优化寻找一个数能交换到哪里的过程。其实我们只要记录下边的先后关系就好了。
假如我们要操作下图(边权表示边的编号,点权表示点的编号,图上没有题目中“节点上数字”):
容易发现边有一下关系:
这些关系比较复杂,要处理好煞是困难。但是S大佬想出了一个极其神奇的方法(假定当前从①交换到⑤):
这样子做就可以了,但是可以发现实现起来相当复杂。以下有一些Tips:
#include
using namespace std;
#define N 2005
struct node
{
int nex,las;
inline void clear(){nex=las=0;}
}link[N][N];//bool print;
int st[N],ans[N],ed[N],id[N],fir[N],to[4005],nex[4005],f[N][N],pre[N],a[N][N],list[N],cnt[N],siz[N][N],deg[N],s;
inline void inc(int x,int y)
{
++deg[x],++deg[y];
to[++s]=y,nex[s]=fir[x],fir[x]=s;
to[++s]=x,nex[s]=fir[y],fir[y]=s;
}
inline int getfather(int i,int k)
{
if(f[i][k]==k) return k;
return f[i][k]=getfather(i,f[i][k]);
}
inline void add(int k,int x,int y)
{
link[k][x].nex=y,link[k][y].las=x;
int fa=getfather(k,y);
f[k][fa]=getfather(k,x);
siz[k][f[k][x]]+=siz[k][fa];
//printf("graph\t%d:\t%d\t->\t%d\n",k,x,y);
}
void dfs(int k,int las)
{
for(int i=fir[k];i;i=nex[i])
if(i>>1!=las&&(link[k][las].nex==i>>1||getfather(k,las)!=getfather(k,i>>1)&&
!link[k][las].nex)&&(link[k][i>>1].las==las||!link[k][i>>1].las))//作为中间经过的点的情况
{
if(las==0&&f[k][i>>1]==getfather(k,ed[k])&&
siz[k][f[k][i>>1]]<deg[k]) continue;//作为起点的情况
if(st[k]&&ed[k]&&f[k][las]==st[k]&&getfather(k,ed[k])==f[k][i>>1]&&
siz[k][st[k]]+siz[k][f[k][i>>1]]<deg[k]) continue;//作为终点的情况
if(to[i]<s&&ed[to[i]]==0&&(i>>1!=st[to[i]]||deg[to[i]]==1))
{
//if(print) printf("\tprint:\t%d\n",to[i]);
if(getfather(to[i],i>>1)==st[to[i]])
{
if(siz[to[i]][st[to[i]]]==deg[to[i]]) s=to[i];
}
else s=to[i];
}
pre[to[i]]=k,cnt[to[i]]=cnt[k]+1;
dfs(to[i],i>>1);
}
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
int t,n,i,j,x,y;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n),s=1;
for(i=1;i<=n;++i)
{
fir[i]=st[i]=ed[i]=deg[i]=0;
for(j=1;j<=n;++j) link[i][j].clear(),f[i][j]=j,siz[i][j]=1;
}
for(i=1;i<=n;++i) scanf("%d",id+i);
for(i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
a[x][y]=a[y][x]=i,inc(x,y);
}
for(i=1;i<=n;++i)
{
for(j=1;j<=n;++j) pre[j]=cnt[j]=0;
s=N,x=id[i],cnt[x]=1;
dfs(x,0),y=s,printf("%d ",s);
for(j=y;j;j=pre[j]) list[cnt[j]]=j;
//printf("%d\t%d\t%d\nlist:\t",i,x,y);
//for(j=1;j<=cnt[y];++j) printf("%d\t",list[j]);puts("");
//printf("graph\t%d:\tst=\t%d\n",x,a[x][list[2]]);
//printf("graph\t%d:\ted=\t%d\n",y,a[y][list[cnt[y]-1]]);
st[x]=a[x][list[2]],ed[y]=a[y][list[cnt[y]-1]];
link[x][st[x]].las=N,link[y][ed[y]].nex=N;
for(j=2;j<cnt[y];++j)
add(list[j],a[list[j-1]][list[j]],a[list[j]][list[j+1]]);//puts("\n");
}
puts("");
}
return 0;
}