http://acm.hdu.edu.cn/showproblem.php?pid=6727
其实本质就是一个中序遍历的问题,我们会尽可能先走向编号最小的点。那么只要用一个计数器来标记每个点的编号就行了。
但是本题有很多坑
当前根节点的编号要比左右子树中最小的点都要小的时候,那么比较左子树右子树的size,先走小的size那边,才能让当前根节点的序号尽可能小。如果sz相等,还是要比较左右子树中最小的点的编号大小。
如果只有一个儿子,那么需要比较当前根节点和子树中最小节点的大小,如果当前根节点更小,那么根节点需要先遍历。
#include<bits/stdc++.h>
#define maxl 100010
using namespace std;
const int mod=1e9+7;
int n,tot,rt;
int ans[maxl],sz[maxl],rudu[maxl];
int miv[maxl][2],mi[maxl];
vector <int> e[maxl];
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
rudu[i]=0,ans[i]=0,e[i].clear(),sz[i]=0;
int v1,v2;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&v1,&v2);
rudu[v2]++;
if(v1>0)
{
rudu[v1]++;
e[i].push_back(v1);
}
if(v2>0)
{
rudu[v2]++;
e[i].push_back(v2);
}
}
for(int i=1;i<=n;i++)
if(rudu[i]==0)
rt=i;
}
inline void pre(int u)
{
int v,l=e[u].size();
mi[u]=u;miv[u][0]=maxl;miv[u][1]=maxl;sz[u]=1;
for(int i=0;i<l;i++)
{
v=e[u][i];
pre(v);sz[u]+=sz[v];
miv[u][i]=mi[v];
mi[u]=min(mi[u],miv[u][i]);
}
if(u<miv[u][0] && u<miv[u][1] && l==2)
{
if(l==2 && sz[e[u][0]]==sz[e[u][1]])
{
if(miv[u][0]>miv[u][1])
{
swap(e[u][0],e[u][1]);
swap(miv[u][0],miv[u][1]);
}
}
else
{
if(l==2 && sz[e[u][0]]>sz[e[u][1]])
{
swap(e[u][0],e[u][1]);
swap(miv[u][0],miv[u][1]);
}
}
}
else
if(l==2 && miv[u][0]>miv[u][1])
{
swap(e[u][0],e[u][1]);
swap(miv[u][0],miv[u][1]);
}
}
inline void dfs(int u)
{
int v,l=e[u].size(),flag=false;
if(l==1 && u<miv[u][0])
{
flag=true;
}
if(flag)
ans[u]=++tot;
if(l>0)
{
v=e[u][0];
dfs(v);
}
if(!flag)
ans[u]=++tot;
if(l>1)
{
v=e[u][1];
dfs(v);
}
}
inline void mainwork()
{
pre(rt);
tot=0;
dfs(rt);
}
inline void print()
{
long long sum=0,tmp=233;
for(int i=1;i<=n;i++)
{
sum=(sum+(ans[i]^i)*tmp%mod)%mod;
tmp=tmp*233%mod;
}
printf("%lld\n",sum);
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}