【题目】
简单版
数据加强版
一颗
找三个不同编号的节点,使它们两两间距离相同(一条边距离视作1),求方案数。
【题目分析】
简化版的我们都会做。
由于可以
发现三个妹子一定在根的三个不同儿子的子树中,所以我们可以遍历根的每个儿子,设
但是到了10w级别,就不能这样了qwq (这不废话吗)
【解题思路】
难度加大以后就变神了啊qwq。%%%题解。
居然是用指针来
我们考虑一下dp,
设
这样的话枚举出边,一边枚举一边更新保证不重复计算,每次枚举出边的时候再枚举
但是这样的话时间和空间都会爆,我们把整个树进行轻重链剖分,子树深度最大的儿子是重儿子,重边练成重链,对于一个点,在第一次用儿子更新的时候我们有
这个时间复杂度是
设
对每个点转移的复杂度为
空间的话非叶子节点所需要的空间都是由他所在重链的叶子节点用指针挪过来的,所以对每个叶子节店给他开正比于所在重链长度的空间即可
空间复杂度为
【参考代码】
BZOJ3522
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5010;
int n,tot;
int head[N],dep[N];
LL ans;
LL s[N],f[4][N];
struct Tway
{
int v,nex;
};
Tway e[N<<1];
void add(int u,int v)
{
e[++tot]=(Tway){v,head[u]};
head[u]=tot;
}
void dfs(int x,int fa)
{
s[dep[x]]++;
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa)
continue;
dep[v]=dep[x]+1;
dfs(v,x);
}
}
int main()
{
// freopen("BZOJ3522.in","r",stdin);
// freopen("BZOJ3522.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
for(int i=1;i<=n;++i)
{
memset(f,0,sizeof(f));
for(int j=1;j<=n;++j)
f[0][j]=1;
for(int j=head[i];j;j=e[j].nex)
{
memset(s,0,sizeof(s));
int v=e[j].v;
dep[v]=1;dfs(v,i);
for(int k=3;k;--k)
for(int q=1;s[q];++q)
f[k][q]+=f[k-1][q]*s[q];
}
for(int j=1;f[3][j];++j)
ans+=f[3][j];
}
printf("%lld\n",ans);
return 0;
}
BZOJ4543
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n,tot;
int head[N],son[N],dep[N],h[N],mx[N],siz[N];
LL id,ans;
LL mem[10*N],*f[N],*g[N];
struct Tway
{
int v,nex;
};
Tway e[N<<1];
void add(int u,int v)
{
e[++tot]=(Tway){v,head[u]};
head[u]=tot;
}
void dfs(int u,int fa)
{
dep[u]=dep[fa]+1;mx[u]=u;
for(int i=head[u];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa)
continue;
dfs(v,u);
if(dep[mx[v]]>dep[mx[son[u]]])
son[u]=v;
if(dep[mx[v]]>dep[mx[u]])
mx[u]=mx[v];
}
siz[mx[u]]=h[u]=dep[mx[u]]-dep[u]+1;
}
void init()
{
for(int i=1;i<=n;++i)
{
if(mx[i]!=i)
continue;
id+=siz[i]-1;
f[i]=&mem[id];++id;
g[i]=&mem[id];id+=2*siz[i]+2;
}
}
void solve(int u,int fa)
{
if(mx[u]==u)
{
f[u][0]=1;
return;
}
solve(son[u],u);
f[u]=f[son[u]]-1;g[u]=g[son[u]]+1;
ans+=g[u][0];f[u][0]=1;
for(int i=head[u];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa || v==son[u])
continue;
solve(v,u);
for(int i=0;i<=h[v];++i)
ans+=f[v][i]*g[u][i+1];
for(int i=1;i<=h[v];++i)
ans+=g[v][i]*f[u][i-1];
for(int i=0;i<=h[v];++i)
g[u][i+1]+=f[v][i]*f[u][i+1];
for(int i=1;i<=h[v];++i)
g[u][i-1]+=g[v][i];
for(int i=0;i<=h[v];++i)
f[u][i+1]+=f[v][i];
}
}
int main()
{
freopen("BZOJ4543.in","r",stdin);
freopen("BZOJ4543.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs(1,0);
init();
solve(1,0);
printf("%lld\n",ans);
return 0;
}
【总结】
至今仍觉得这个时间复杂度的证明很妙啊!
然后这个指针的