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

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

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

文章目录

  • P8973 『GROI-R1』 继续深潜,为了同一个梦想
    • 题目大意
    • 思路
    • code

题目大意

我称一棵树上的一个点集是“连接的”,当且仅当树上存在一条链能够覆盖这个点集并且这个集合大小不小于 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 
#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;
}

你可能感兴趣的:(dp,tree,题解,算法,动态规划,c++,数据结构)