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

27 篇文章 0 订阅
13 篇文章 0 订阅

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

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

题目大意

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

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

a n s i ans_i ansi 为包含 i i i 号节点的连接的集合的个数对 1 0 9 + 7 10^9+7 109+7 取模后的值

输出 x o r i = 1 n a n s i ∗ i xor_{i = 1}^n ans_i * i xori=1nansii

思路

换根 d p dp dp

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

那么:
f r ( x ) = ∑ u ∈ s o n ( x ) f r ( u ) ∗ 2 + 1 f_r(x) = \sum_{u\in son(x)} f_r(u) * 2 + 1 fr(x)=uson(x)fr(u)2+1
因为 u u u 点可以选也可以不选,并且可以选 { u , x } \{u , x\} {u,x}

我算出所有 f f f 后考虑以 r r r 为根时的答案 a n s r ans_r ansr ,设 g r ( u ) = f r ( u ) ∗ 2 + 1 g_r(u) = f_r(u) * 2 + 1 gr(u)=fr(u)2+1

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

那么:
a n s r = ∑ u , v ∈ s o n ( r ) , u ≠ v g r ( u ) ∗ g r ( v ) + ∑ u ∈ s o n ( r ) g r ( u ) ans_r = \sum_{u , v \in son(r) , u \neq v} g_r(u) * g_r(v) + \sum _{u \in son(r)} g_r(u) ansr=u,vson(r),u=vgr(u)gr(v)+uson(r)gr(u)
s r = ∑ u ∈ s o n ( r ) g r ( u ) s_r = \sum_{u \in son(r)} g_r(u) sr=uson(r)gr(u)

那么:
a n s r = ∑ u ∈ s o n ( r ) g r ( u ) ∗ ( s r − g r ( u ) + 1 ) ans_r = \sum_{u\in son(r)} g_r(u) * (s_r - g_r(u) + 1) ansr=uson(r)gr(u)(srgr(u)+1)
实现的时候两条链组成一条新的链时不要多加了

接下来考虑换根, u → v u\to v uv 换根,考虑 f f f 的变化
f v ( u ) = f u ( u ) − ( f u ( v ) ∗ 2 + 1 ) f v ( v ) = f u ( v ) + f v ( u ) ∗ 2 + 1 f_v(u) =f_u(u) - (f_u(v) * 2 + 1) \newline f_v(v) = f_u(v) +f_v(u) * 2 +1 fv(u)=fu(u)(fu(v)2+1)fv(v)=fu(v)+fv(u)2+1
其他的点 x x x 就是 f v ( x ) = f u ( x ) f_v(x) = f_u(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;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值