XSY 1749 tree

题目大意

给定一棵基环树, 问你有多少条路径的长度\(\ge K\).

点数\(\le 10^5\)

Solution

基环树分治模板题.

我是这样做的: 加边的时候用并查集维护点的连通性, 少加入环上的一条边, 使得基环图变为树的形态.

首先在树上进行一次常规的树分治. 我们能得到不经过被删除的一条边的满足要求的路径数量;

然后我们根据被删去的边的两个端点找到环, 在环上求出经过被删去边的路径数量即可.

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>

using namespace std;
namespace Zeonfai
{
    inline int getInt()
    {
        int a = 0, sgn = 1; char c;
        while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
        while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
        return a * sgn;
    }
}
const int N = (int)4e5;
int n, m, len;
int lp[2];
long long ans;
struct disjointSet
{
    int pre[N + 1];
    inline void initialize() {for(int i = 1; i <= n; ++ i) pre[i] = i;}
    inline int access(int u)
    {
        if(pre[u] != u) pre[u] = access(pre[u]);
        return pre[u];
    }
}st;
struct segmentTree
{
    struct node
    {
        node *suc[2];
        int sz;
        inline node() {for(int i = 0; i < 2; ++ i) suc[i] = NULL; sz = 0;}
    }*rt;
    void clear(node *u)
    {
        for(int i = 0; i < 2; ++ i) if(u->suc[i] != NULL) clear(u->suc[i]);
        delete u;
    }
    inline void clear()
    {
        if(rt != NULL) clear(rt); rt = NULL; // 记得要把rt复位, 否则就会乱套啦
    }
    node* modify(node *u, int L, int R, int pos, int dlt)
    {
        if(u == NULL) u = new node;
        u->sz += dlt;
        if(L == R) return u;
        if(pos <= L + R >> 1) u->suc[0] = modify(u->suc[0], L, L + R >> 1, pos, dlt);
        else u->suc[1] = modify(u->suc[1], (L + R >> 1) + 1, R, pos, dlt);
        return u;
    }
    inline void modify(int pos, int dlt)
    {
        rt = modify(rt, 1, n, pos, dlt);
    }
    int query(node *u, int L, int R, int pos)
    {
        if(u == NULL) return 0;
        if(L >= pos) return u->sz;
        if(pos > L + R >> 1) return query(u->suc[1], (L + R >> 1) + 1, R, pos);
        else return query(u->suc[0], L, L + R >> 1, pos) + query(u->suc[1], (L + R >> 1) + 1, R, pos);
    }
    inline int query(int pos)
    {
        return query(rt, 1, n, pos);
    }
}seg;
struct tree
{
    struct node
    {
        vector<int> edg;
        int flg, mx, sz;
        int nxt, lst, len;
        inline node() {edg.clear(); flg = 0;}
    }nd[N + 1];
    inline void addEdge(int u, int v)
    {
        int rtU = st.access(u), rtV = st.access(v);
        if(rtU == rtV)
        {
            lp[0] = u, lp[1] = v;
            return;
        }
        st.pre[rtU] = rtV;
        nd[u].edg.push_back(v); nd[v].edg.push_back(u);
    }
    void getSize(int u, int pre)
    {
        // printf("%d\n", u);
        nd[u].sz = 1; nd[u].mx = 0;
        for(auto v : nd[u].edg) if(v != pre && ! nd[v].flg) getSize(v, u), nd[u].sz += nd[v].sz, nd[u].mx = max(nd[v].sz, nd[u].mx);
        // for(vector<int>::iterator p = nd[u].edg.begin(); p != nd[u].edg.end(); ++ p)
            // if(*p != pre && ! nd[*p].flg) getSize(*p, u), nd[u].sz += nd[*p].sz, nd[u].mx = max(nd[*p].sz, nd[u].mx);
    }
    int getRoot(int u, int pre, int cen)
    {
        nd[u].mx = max(nd[u].mx, nd[cen].sz - nd[u].sz);
        int res = u;
        for(auto v : nd[u].edg) if(v != pre && ! nd[v].flg)
        {
            int cur = getRoot(v, u, cen);
            if(nd[cur].mx < nd[res].mx) res = cur;
        }
        return res;
    }
    void getAnswer(int u, int pre, int cur)
    {
        ans += seg.query(len - cur);
        for(auto v : nd[u].edg) if(v != pre && ! nd[v].flg) getAnswer(v, u, cur + 1);
    }
    void update(int u, int pre, int cur)
    {
        seg.modify(cur, 1);
        for(auto v : nd[u].edg) if(v != pre && ! nd[v].flg) update(v, u, cur + 1);
    }
    inline void work(int u)
    {
        getSize(u, -1);
        u = getRoot(u, -1, u); nd[u].flg = 1;
        seg.clear(); seg.modify(1, 1);
        for(auto v : nd[u].edg) if(! nd[v].flg)
        {
            getAnswer(v, u, 1);
            update(v, u, 2);
        }
        for(auto v : nd[u].edg) if(! nd[v].flg) work(v);
    }
    inline void work() {work(1);}
    int getLoop(int u, int pre)
    {
        nd[u].lst = pre; nd[u].nxt = -1;
        if(u == lp[1]) return u;
        for(auto v : nd[u].edg) if(v != pre && nd[u].nxt == -1) nd[u].nxt = getLoop(v, u);
        if(~ nd[u].nxt) return u;
        nd[u].lst = -1;
        return -1;
    }
    void getOutsideSize(int u, int pre, int cur, int opt)
    {
        seg.modify(cur, opt);
        for(auto v : nd[u].edg) if(v != pre) getOutsideSize(v, u, cur + 1, opt);
    }
    void getOutsideSize(int u, int cur)
    {
        seg.modify(cur, 1);
        nd[u].len = cur;
        for(auto v : nd[u].edg) if(v != nd[u].nxt && v != nd[u].lst) getOutsideSize(v, u, cur + 1, 1);
        if(~ nd[u].lst) getOutsideSize(nd[u].lst, cur + 1);
    }
    void getLoopAnswer(int u, int pre, int cur)
    {
        ans += seg.query(len - cur);
        for(auto v : nd[u].edg) if(v != pre) getLoopAnswer(v, u, cur + 1);
    }
    void getLoopAnswer(int u, int cur)
    {
        seg.modify(nd[u].len, -1);
        for(auto v : nd[u].edg) if(v != nd[u].nxt && v != nd[u].lst) getOutsideSize(v, u, nd[u].len + 1, -1);
        ans += seg.query(len - cur);
        for(auto v : nd[u].edg) if(v != nd[u].nxt && v != nd[u].lst) getLoopAnswer(v, u, cur + 1);
        if(~ nd[u].nxt) getLoopAnswer(nd[u].nxt, cur + 1);
    }
    inline void workOnLoop()
    {
        getLoop(lp[0], -1);
        seg.clear();
        getOutsideSize(lp[1], 1);
        getLoopAnswer(lp[0], 1);
    }
}T;
int main()
{

#ifndef ONLINE_JUDGE

    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);

#endif

    using namespace Zeonfai;
    n = getInt(), m = getInt(), len = getInt();
    st.initialize();
    for(int i = 0, u, v; i < m; ++ i) u = getInt(), v = getInt(), T.addEdge(u, v);
    T.work();
    if(n == m) T.workOnLoop();
    printf("%lld\n", ans);
}

posted @ 2017-09-13 16:10  Zeonfai  阅读(226)  评论(0编辑  收藏  举报