Dominant Indices
这个题显然可以启发式合并做掉。
时间复杂度是 $O(n\log n)$ 的。
满足于这个题肯定已经够了,但还有更快的方法:长链剖分。
长链剖分,顾名思义就是找到每个点的长儿子(深度最深),把整棵树剖成长链。
OI wiki 上有讲,这种方法的跳链一类的复杂度是 $O(\sqrt{n})$ 级别的,而且能卡。
但我们这题显然不是利用这个性质。
我们有一个最简单的 DP。
定义 $f(p,j)$ 表示离节点 $p$ 距离为 $j$ 的节点个数,则有:
$$f(p,j)=\sum_{v}f(v,j-1)$$
特别的,我们有初始化:
$$f(p,0)=1$$
即距离为 $0$ 的节点只有 $p$ 自己。
我们发现这个转移的瓶颈在于每个节点都要 $O(n)$ 的合并。
然后我们就想到一个偷懒的方法,那就是根本不去合并,而是把长儿子的答案直接存在自己的答案里,然后把非长儿子的信息暴力合并进来。
可能刚听到这个想法大部分人反应不过来,实际上这是一个用指针动态分配内存的操作。直接叙述实际上稍显累赘,看代码的话会更为清晰(只可意会不可言传)。
比如说,我们开一串数组,给下面的动态分配内存留下一个池子。
接着我们给 $p$ 分配一个长度为 $dep(p)$ 的内存空间。在这个空间上,我们直接把 $p$ 的长儿子的答案存在这段空间的上,不过对应的下标要错开一位。
接着,我们把非长儿子的答案暴力合并,也就是去做上述的 DP。
可能在直觉上,很多人觉得这个方法的复杂度大概还是 $O(n^2)$ 级别的。
但实际上,这个优化把空间和时间复杂度全部优化到了 $O(n)$ 级别。
先说时间上,我们真正的复杂度开销全在暴力合并这一个环节上。我们不从 $p$ 考虑,而去从它的非长儿子考虑。每个点会被暴力合并,当且仅当它是一个非长儿子,即它是一条长链的顶点。那么时间复杂度就与长链数线性相关。而长链数是 $O(n)$ 级别的(我这里说的是严格的 $O$ 而不是 $\Theta$,因为从这个角度来考虑,我们的开销就全在长儿子的计算上了,而长儿子的计算显然是 $\Theta(n)$ 的)。
那么接下来看空间,我们相当于给每个长链申请了一片大小为长链长度的内存,所以最后所需的内存大小即为树的大小,空间复杂度也是 $O(n)$ 的。
至此,这道题目得到了 $O(n)$ 的解决。
代码(长链剖分优化):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| #include<iostream> #include<cstdio> #define ll long long using namespace std;
const ll N=1e6;
ll n,u,v,tot;
ll buf[N+5];
ll *f[N+5],*now=buf;
ll ver[N*2+5],nxt[N*2+5],head[N+5];
ll ans[N+5],dep[N+5],ls[N+5];
inline void dfs(ll p,ll fath) { for(ll i=head[p];i;i=nxt[i]) { if(ver[i]==fath) continue; dfs(ver[i],p); if(dep[ver[i]]>dep[ls[p]]) ls[p]=ver[i]; } dep[p]=dep[ls[p]]+1; }
inline void dfs_(ll p,ll fath) { f[p][0]=1; if(ls[p]) { f[ls[p]]=f[p]+1; dfs_(ls[p],p); ans[p]=ans[ls[p]]+1; } for(ll i=head[p];i;i=nxt[i]) { if(ver[i]==fath||ver[i]==ls[p]) continue; f[ver[i]]=now;now+=dep[ver[i]]; dfs_(ver[i],p); for(ll j=1;j<=dep[ver[i]];j++) { f[p][j]+=f[ver[i]][j-1]; if(f[p][j]>f[p][ans[p]]||(f[p][j]==f[p][ans[p]]&&j<ans[p])) ans[p]=j; } } if(f[p][ans[p]]==1) ans[p]=0; }
inline void addedge(ll u,ll v) { ver[++tot]=v;nxt[tot]=head[u];head[u]=tot; }
inline ll read() { ll ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();} while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();} return ret*f; }
inline void write(ll x) { static char buf[22];static ll len=-1; if(x>=0) { do{buf[++len]=x%10+48;x/=10;}while(x); } else { putchar('-'); do{buf[++len]=-(x%10)+48;x/=10;}while(x); } while(len>=0) putchar(buf[len--]); }
inline void writeln(ll x) { write(x);putchar('\n'); }
int main() {
n=read();
for(ll i=1;i<n;i++) { u=read();v=read(); addedge(u,v);addedge(v,u); }
dfs(1,0); f[1]=now;now+=dep[1]; dfs_(1,0);
for(ll i=1;i<=n;i++) {writeln(ans[i]);}
return 0; }
|
代码(启发式合并):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| #include<iostream> #include<cstdio> #define ll long long using namespace std;
const ll N=1e6;
ll n,u,v,tot,tmpans;
ll siz[N+5],dt[N+5],hs[N+5],ans[N+5],cnt[N+5];
ll ver[N*2+5],nxt[N*2+5],head[N+5];
inline void dfs(ll p,ll fath) { siz[p]=1;dt[p]=dt[fath]+1; for(ll i=head[p];i;i=nxt[i]) { if(ver[i]==fath) continue; dfs(ver[i],p); if(siz[ver[i]]>siz[hs[p]]) hs[p]=ver[i]; siz[p]+=siz[ver[i]]; } }
inline void getans(ll p,ll fath) { cnt[dt[p]]++; if(cnt[dt[p]]>cnt[tmpans]) tmpans=dt[p]; else if(cnt[dt[p]]==cnt[tmpans]&&dt[p]<tmpans) tmpans=dt[p]; for(ll i=head[p];i;i=nxt[i]) { if(ver[i]==fath) continue; getans(ver[i],p); } }
inline void clear(ll p,ll fath) { cnt[dt[p]]--; for(ll i=head[p];i;i=nxt[i]) { if(ver[i]==fath) continue; clear(ver[i],p); } }
inline void dfs_(ll p,ll fath,bool kp) { for(ll i=head[p];i;i=nxt[i]) { if(ver[i]==fath||ver[i]==hs[p]) continue; dfs_(ver[i],p,0); } if(hs[p]) dfs_(hs[p],p,1); cnt[dt[p]]++; if(cnt[dt[p]]>cnt[tmpans]) tmpans=dt[p]; else if(cnt[dt[p]]==cnt[tmpans]&&dt[p]<tmpans) tmpans=dt[p]; for(ll i=head[p];i;i=nxt[i]) { if(ver[i]==fath||ver[i]==hs[p]) continue; getans(ver[i],p); } ans[p]=tmpans-dt[p]; if(!kp) clear(p,fath); }
inline void addedge(ll u,ll v) { ver[++tot]=v;nxt[tot]=head[u];head[u]=tot; }
inline ll read() { ll ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();} while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();} return ret*f; }
inline void write(ll x) { static char buf[22];static ll len=-1; if(x>=0) { do{buf[++len]=x%10+48;x/=10;}while(x); } else { putchar('-'); do{buf[++len]=-(x%10)+48;x/=10;}while(x); } while(len>=0) putchar(buf[len--]); }
inline void writeln(ll x) { write(x);putchar('\n'); }
int main() {
n=read();
for(ll i=1;i<n;i++) { u=read();v=read(); addedge(u,v);addedge(v,u); }
dfs(1,0);dfs_(1,0,1);
for(ll i=1;i<=n;i++) { writeln(ans[i]); }
return 0; }
|