P5002 专心OI - 找祖先

显然的数论水题吧

这道题一开始来看的话要看一下,然后就能发现这是个数学问题。

显然要是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;
}
posted @ 2018-11-06 21:44  Garen-Wang  阅读(133)  评论(0编辑  收藏  举报