这道题我是真的服了。
好不容易想起来启发式合并,然后顺着思路想到了一个清奇的解法,对于每个点开一个map,m[u](dep[u],num)存的是u的子树中,深度为dep[u]的点的个数,这样对于每个点的合并,先直接找到包含最长链的那个子节点v,m[u]=m[v],因为最长链就代表了又更多的dep值,而且这个是一个指针操作,复杂度是O(1)的,(没有证实,以前好像挺学长讲过这个),map里存的东西也就越多,然后再将其他子节点的map全部遍历合并到,这就是一个启发式合并,将小的块合并到大的块。
是啊我也觉得很正确啊,然后mle on test105,卧槽512mb都不够用,没办法尝试一下清掉最长子节点的链的map值,于是空间不爆了,然而tle on test 111。没办法,clear是线性复杂度,这样的话相当于遍历了一遍大块,满足不了启发式合并的性质,于是我这个清奇的想法就GG了QAQ,贴个代码纪念一蛤
-----------------------------------------------------------------------------------------------------------------------------------------
update:结果。。。swap(m[v],m[u])就不用清空了,天哪我写的什么垃圾启发式合并啊。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
//#include<unordered_map>
#define maxl 1000010
using namespace std;
int n,cnt=0;
int dep[maxl],hvyson[maxl],hvy[maxl],maxd[maxl],ans[maxl],ehead[maxl],sz[maxl];
struct ed
{
int to,nxt;
}e[maxl<<1];
map<int,int> m[maxl];
map<int,int>:: iterator it,it1,it2;
inline void add(int u,int v)
{
e[++cnt].to=v;e[cnt].nxt=ehead[u];ehead[u]=cnt;
}
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
void dfs(int u,int fa)
{
int v;hvy[u]=dep[u];
for(int i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(v==fa) continue;
dep[v]=dep[u]+1;
dfs(v,u);
if(hvy[v]>hvy[u])
{
hvyson[u]=v;
hvy[u]=hvy[v];
}
}
}
inline void prework()
{
n=read();
int u,v;
for(int i=1;i<=n-1;i++)
{
u=read();v=read();
add(u,v);
add(v,u);
}
dep[1]=1;
dfs(1,0);
}
void solve(int u,int fa)
{
int v,k;
for(int i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(v==fa) continue;
solve(v,u);
}
if(hvyson[u]>0)
{
v=hvyson[u];
swap(m[u],m[v]);
// m[v].clear();
m[u][dep[u]]=1;
if(maxd[v]>1)
{
maxd[u]=maxd[v];
ans[u]=ans[v]+1;
}
else
maxd[u]=1,ans[u]=0;
}
else
m[u][dep[u]]=1,maxd[u]=1,ans[u]=0;
for(int i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(v==hvyson[u] || v==fa) continue;
for(it=m[v].begin();it!=m[v].end();it++)
{
k=it->first;
m[u][k]+=m[v][k];
if(m[u][k]>maxd[u])
{
maxd[u]=m[u][k];
ans[u]=k-dep[u];
}
if(m[u][k]==maxd[u] && k-dep[u]<ans[u])
ans[u]=k-dep[u];
}
}
}
inline void mainwork()
{
solve(1,0);
}
inline void print()
{
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
}
int main()
{
// freopen("F.in","r",stdin);
// freopen("F.out","w",stdout);
prework();
mainwork();
print();
return 0;
}
看了网上的题解http://www.cnblogs.com/widsom/p/9317788.html
他直接利用轻重链的性质合并,并不考虑map里存的东西的个数,这样每次只清除轻链的map,而重链的map直接map保留然后暴力dfs跑轻链加到map里去,这样确实就和树链剖分一样了,能够保证只遍历nlogn的节点,而且map只要用一个,也不涉及合并,直接暴力dfs网上加。
#include<cstdio>
#include<cstring>
#include<map>
#define maxl 1000010
using namespace std;
int n,cnt=0,mx=0;
int ehead[maxl],son[maxl],hvy[maxl],dep[maxl],ans[maxl];
struct ed
{
int to,nxt;
}e[maxl<<1];
map <int,int> m;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int u,int v)
{
e[++cnt].to=v;e[cnt].nxt=ehead[u];ehead[u]=cnt;
}
void dfs(int u,int fa)
{
int v;son[u]=1;
for(int i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(v==fa) continue;
dep[v]=dep[u]+1;
dfs(v,u);
son[u]+=son[v];
if(son[v]>son[hvy[u]])
hvy[u]=v;
}
}
inline void prework()
{
int u,v;
n=read();
for(int i=1;i<=n-1;i++)
{
u=read();v=read();
add(u,v);add(v,u);
}
dep[1]=1;
dfs(1,0);
}
void dfs2(int u,int fa)
{
m[dep[u]]++;
if(m[dep[u]]>m[mx])
mx=dep[u];
if(m[dep[u]]==m[mx] && dep[u]<mx)
mx=dep[u];
int v;
for(int i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(v==fa) continue;
dfs2(v,u);
}
}
void solve(int u,int fa)
{
int v=0;
for(int i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(v==fa || v==hvy[u]) continue;
solve(v,u);
mx=0;
m.clear();
}
if(hvy[u])
solve(hvy[u],u);
for(int i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(v==fa || v==hvy[u]) continue;
dfs2(v,u);
}
m[dep[u]]++;
if(m[dep[u]]>m[mx])
mx=dep[u];
if(m[dep[u]]==m[mx] && dep[u]<mx)
mx=dep[u];
ans[u]=mx-dep[u];
}
inline void mainwork()
{
solve(1,0);
}
inline void print()
{
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
}
int main()
{
prework();
mainwork();
print();
return 0;
}