P8973 『GROI-R1』 继续深潜,为了同一个梦想

P8973 『GROI-R1』 继续深潜,为了同一个梦想

P8973 『GROI-R1』 继续深潜,为了同一个梦想 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目大意

我称一棵树上的一个点集是“连接的”,当且仅当树上存在一条链能够覆盖这个点集并且这个集合大小不小于 2

对于每个点,询问每个点被多少个这样的点集所包含

ansi 为包含 i 号节点的连接的集合的个数对 109+7 取模后的值

输出 xori=1nansii

思路

换根 dp

fr(x) 为以 r 为根时,以 x 为一个端点,在 x 的子树里面选 1 个点,满足这些点在同一条链上面的方案数。

那么:

fr(x)=uson(x)fr(u)2+1

因为 u 点可以选也可以不选,并且可以选 {u,x}

我算出所有 f 后考虑以 r 为根时的答案 ansr ,设 gr(u)=fr(u)2+1

其实就是 fr(r) 加上两条链连起来的总和。

那么:

ansr=u,vson(r),uvgr(u)gr(v)+uson(r)gr(u)

sr=uson(r)gr(u)

那么:

ansr=uson(r)gr(u)(srgr(u)+1)

实现的时候两条链组成一条新的链时不要多加了

接下来考虑换根, uv 换根,考虑 f 的变化

fv(u)=fu(u)(fu(v)2+1)fv(v)=fu(v)+fv(u)2+1

其他的点 x 就是 fv(x)=fu(x)

code

#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
#define LL long long
using namespace std;
const int N = 5e5 + 5;
const LL mod = 1e9 + 7;
int hd[N] , cnt , n , fa[N];
LL f[N] , ans[N];
struct E {
    int to , nt;
} e[N << 1];
void add (int x , int y) { e[++cnt].to = y , e[cnt].nt = hd[x] , hd[x] = cnt; }
void dfs1 (int x) {
    int y;
    for (int i = hd[x] ; i ; i = e[i].nt) {
        y = e[i].to;
        if (y == fa[x]) continue;
        fa[y] = x;
        dfs1 (y);
        f[x] = (f[x] + f[y] * 2 % mod + 1) % mod;
    }
}
void dfs2 (int x) {
    int y;
    LL sum = 0;
    for (int i = hd[x] ; i ; i = e[i].nt) {
        y = e[i].to;
        sum = (sum + 2 * f[y] % mod + 1) % mod;
    }
    LL g;
    for (int i = hd[x] ; i ; i = e[i].nt) {
        y = e[i].to;
        g = (f[y] * 2 % mod + 1) % mod;
        ans[x] = (ans[x] + g * ((sum - g + mod + 1) % mod) % mod) % mod;
        sum = (sum - g + mod) % mod;
    }
    LL xx , yy;
    for (int i = hd[x] ; i ; i = e[i].nt) {
        y = e[i].to;
        if (y == fa[x]) continue;
        xx = f[x] , yy = f[y];
        f[x] = (xx + mod - (yy * 2 % mod + 1) % mod) % mod;
        f[y] = (yy + f[x] * 2 % mod + 1) % mod;
        dfs2 (y);
        f[x] = xx , f[y] = yy;
    }
}
int main () {
    int u , v;
    scanf ("%d" , &n);
    fu (i , 1 , n - 1) {
        scanf ("%d%d" , &u , &v);
        add (u , v) , add (v , u);
    }
    dfs1 (1);
    dfs2 (1);
    LL ans1 = ans[1] * 1;
    fu (i , 2 , n) ans1 = ans1 ^ (ans[i] * i);
    printf ("%lld" , ans1);
    return 0;
}
posted @   2020fengziyang  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示