G. Rikka with Intersections of Paths(2018-2019ICPC徐州)【树链剖分+线段树】

G. Rikka with Intersections of Paths(2018-2019ICPC徐州)【树链剖分+线段树】

题意

给定一棵 n n n 个点的树和 m m m 个树上的简单路径的端点点对,求从 m m m 条路里选 k k k 条,满足这 k k k 条路有至少一个点是公共点的方案数。

思路

歪掉的思路

模拟赛的时候想着想着就以为是 k k k 条路连通了,于是写了个单点修改、区间查询的树链剖分,然后对路径排序,按照 l c a ( u , v ) lca(u, v) lca(u,v) 的深度从深往浅处理,每次先统计这条路径上的和 c n t cnt cnt区间查询,贡献为 C ( c n t , k − 1 ) C(cnt, k - 1) C(cnt,k1)),然后对 l c a ( u , v ) lca(u, v) lca(u,v) 的点加1(单点修改),答案非常正确,思路也是没错的,就是可惜看错了题意(呜呜)比赛的时候还以为我树链码炸了qwq

正确的思路

最后发现题解竟然是把我们赛时的思路完全倒过来,区间修改、单点查询,对路径的排序按照 l c a ( u , v ) lca(u, v) lca(u,v) 的深度从浅到深处理,每次计算 l c a ( u , v ) lca(u, v) lca(u,v) 的点的和 c n t cnt cnt单点查询,贡献为 C ( c n t , k − 1 ) C(cnt, k - 1) C(cnt,k1)),然后对路径上的所有点加1(区间修改

改完一发过,很淦,究极板子叠加题

代码

#include
using namespace std;
typedef long long ll;
const int N = 4e5 + 10;
const double pi = acos(-1.0);
const double eps = 1e-7;
const ll mod = 1e9 + 7;

struct line //线段树
{
    struct node
    {
        int l, r;
        int add;
        ll sum;
    } tr[N * 4];
    void pushup(int u)
    {
        tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    }
    void pushdown(int u)
    {
        if(!tr[u].add)
            return ;
        auto &root = tr[u], &lson = tr[u << 1], &rson = tr[u << 1 | 1];
        lson.add += root.add;
        rson.add += root.add;
        lson.sum += (lson.r - lson.l + 1) * root.add;
        rson.sum += (rson.r - rson.l + 1) * root.add;
        root.add = 0;
    }
    void build(int u, int l, int r)
    {
        tr[u] = {l, r, 0, 0};
        if(l == r)
            return ;
        int mid = (l + r) >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
    }
    ll query(int u, int pos)
    {
        if(tr[u].l == pos && tr[u].r == pos)
        {
            return tr[u].sum;
        }
        else
        {
            pushdown(u);
            ll res = 0;
            int mid = (tr[u].l + tr[u].r) >> 1;
            if(pos <= mid)
                res = query(u << 1, pos);
            if(pos > mid)
                res += query(u << 1 | 1, pos);
            return res;
        }
    }
    void modify(int u, int l, int r, int val) ///tr[u] += val
    {
        if(tr[u].l >= l && tr[u].r <= r)
        {
            tr[u].sum += (tr[u].r - tr[u].l + 1) * val;
            tr[u].add += val;
        }
        else
        {
            pushdown(u);
            int mid = (tr[u].l + tr[u].r) >> 1;
            if(l <= mid)
                modify(u << 1, l, r, val);
            if(r > mid)
                modify(u << 1 | 1, l, r, val);
            pushup(u);
        }
    }
} line;

//下面是树链剖分

int head[N * 2], nxt[N * 2], es[N * 2],
    dfsn[N], wson[N], top[N], sz[N], par[N], dep[N], idx, tot;

void addEdge(int u, int v)
{
    es[++idx] = v;
    nxt[idx] = head[u];
    head[u] = idx;
}

void dfs1(int u, int fa)
{
    par[u] = fa;
    dep[u] = dep[fa] + 1;
    sz[u] = 1;
    for(int i = head[u]; ~i; i = nxt[i])
    {
        int v = es[i];
        if(v == fa)
            continue;
        dfs1(v, u);
        sz[u] += sz[v];
        if(wson[u] == 0 || sz[wson[u]] < sz[v])
            wson[u] = v;
    }
}

void dfs2(int u, int tp)
{
    dfsn[u] = ++tot;
    top[u] = tp;
    if(wson[u])
    {
        dfs2(wson[u], tp);
    }
    for(int i = head[u]; ~i; i = nxt[i])
    {
        int v = es[i];
        if(v == par[u] || v == wson[u])
            continue;
        dfs2(v, v);
    }
}

int lca(int u, int v)
{
    while(top[u] != top[v])
    {
        if(dep[top[u]] >= dep[top[v]])
            u = par[top[u]];
        else
            v = par[top[v]];
    }
    return dep[u] > dep[v] ? v : u;
}

ll query(int a, int b)
{
    int res = lca(a, b);
    return line.query(1, dfsn[res]);
}

void update(int a, int b)
{
    ll res = 0;
    while(top[a] != top[b])
    {
        if(dep[top[a]] < dep[top[b]])
            swap(a, b);
        line.modify(1, dfsn[top[a]], dfsn[a], 1);
        a = par[top[a]];
    }
    if(dep[a] > dep[b])
        swap(a, b);
    line.modify(1, dfsn[a], dfsn[b], 1);
}

// 路径的结构体

struct path
{
    int u, v, d;
    bool operator < (const path& obj)
    {
        return d < obj.d;
    }
};

path vec[N];

// 组合数

ll inv[N], fac[N], ifac[N];

void init_C()
{
    fac[0] = ifac[0] = inv[0] = inv[1] = 1;
    for(int i = 2; i <= N - 1; i++)
    {
        inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod;
    }
    for(int i = 1; i <= N - 1; i++)
    {
        fac[i] = 1LL * fac[i - 1] * i % mod;
        ifac[i] = 1LL * ifac[i - 1] * inv[i] % mod;
    }
}

void init()
{
    tot = 0;
    idx = 0;
    memset(head, -1, sizeof head);
    memset(wson, 0, sizeof wson);
}

ll C(ll n, ll m)
{
    if(n < m)
        return 0;
    if(m == 0 || n == m)
        return 1;
    return 1LL * fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}

int main()
{
    init_C();
    int t;
    scanf("%d", &t);
    while (t--)
    {
        init();
        int n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 0, u, v; i < n - 1; i++)
        {
            scanf("%d%d", &u, &v);
            addEdge(u, v);
            addEdge(v, u);
        }
        dfs1(1, 0);
        dfs2(1, 1);
        line.build(1, 1, n);
        for(int i = 0, u, v; i < m; i++)
        {
            scanf("%d%d", &u, &v);
            vec[i] = {u, v, dep[lca(u, v)]};
        }
        sort(vec, vec + m);
        ll ans = 0;
        for(int i = 0; i < m; i++)
        {
            ll res = query(vec[i].u, vec[i].v);
            ans = (ans + C(res, k - 1)) % mod;
            update(vec[i].u, vec[i].v);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

/*
*/

你可能感兴趣的:(血压题,数据结构,树链剖分,线段树)