显然的数论水题吧
这道题一开始来看的话要看一下,然后就能发现这是个数学问题。
显然要是LCA一定在她的子树内,不然不可能她是LCA。
所以先把特殊情况搞掉:自己跟自己只有1种情况,自己跟子树内其他的节点加上双向共有\(2 \times (size[u]-1)\)种情况。
剩下来的就是那些两个不同子树内的节点对数了。
这个情况直接让我联想到“联合权值”那道题,因为有这个数学结论:
\[2(ab+ac+ad+...+bc+bd+...+cd+...)=(a+b+c+d+..)^2-a^2-b^2-c^2-...\]
通过这个公式我们就能求出两两组合方案数的两倍,刚好是我们要求的。
把答案再加上这个就可以了。应该要开long long吧。
总结失误环节
我在看题之前就扫了一眼数据范围:\(n \leq 10000, m \leq 50000\)。
然后每一次询问是针对一个节点,顶多就问10000次啊!
刚开始我就这么想,但是想深入之后就已经忘记有这么回事,最终导致不能1A。
启示:做题的时候用草稿纸记录下你的思路,或许你的零散的思路会成为A题的重要细节。
代码;
#include<cstdio>
#include<cstring>
const int maxn = 10005, maxm = 50005;
struct Edges
{
int next, to;
} e[maxm << 1];
int head[maxn], tot;
int dep[maxn], size[maxn], fa[maxn], wson[maxn];
int top[maxn];
long long ans[maxn];
int n, m, r;
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0', ch = getchar();
return s * ans;
}
void link(int u, int v)
{
e[++tot] = (Edges){head[u], v};
head[u] = tot;
}
void dfs1(int u, int f)
{
dep[u] = dep[f] + 1; size[u] = 1; fa[u] = f;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(v == f) continue;
dfs1(v, u);
size[u] += size[v];
if(size[v] > size[wson[u]]) wson[u] = v;
}
}
void dfs2(int u, int topf)
{
top[u] = topf;
if(wson[u]) dfs2(wson[u], topf);
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(v == fa[u] || v == wson[u]) continue;
dfs2(v, v);
}
}
int main()
{
memset(ans, -1, sizeof ans);
n = read(), r = read(), m = read();
for(int i = 1; i < n; i++)
{
int u = read(), v = read();
link(u, v); link(v, u);
}
dfs1(r, 0); dfs2(r, r);
while(m--)
{
int u = read();
if(ans[u] != -1)
{
printf("%lld\n", ans[u]);
continue;
}
ans[u] = 1 + (size[u] - 1) * 2;
long long temp1 = 0, temp2 = 0;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(v == fa[u]) continue;
temp1 += size[v] * size[v];
temp2 += size[v];
}
ans[u] += temp2 * temp2 - temp1;
printf("%lld\n", ans[u]);
}
return 0;
}