[噼昂!]txdy(Pending)

目录

壹、关于题目 ¶

没时间去编了。

贰、关于题解 ¶

比较经典地,移动右端点指针,维护合法左端点。

左端点合法,指将 \([l,r]\) 的点都保留下来不成环且每个点度数小于等于 \(2\).

然后我们需要维护合法的左端点,一个左端点合法,当且仅当将 \([l,r]\) 的点都保留下来之后,有 \(|V_l|-|E_l|=1\).

综上,我们只需要处理三个部分:

  • 动态维护 \(d_p(p\in[l,r])\);
  • 动态维护连通性;
  • 动态维护 \(|V_l|-|E_l|\);

第一个很好维护,第二个可以考虑使用 \(\rm LCT\),第三个可以考虑线段树。

最后,合法的左端点就是线段树中 \(|V|-|E|=1\) 的部分,并且,由于 \(|V|-|E|\ge 1\),所以我们线段树实际上可以只维护最小值及其个数即可。

时间复杂度 \(\mathcal O(n\log n)\),实际跑得快不快看你的 \(\rm LCT\).

叁、参考代码 ¶

#pragma GCC optimize("Ofast")

#include <bits/stdc++.h>
using namespace std;
 
// # define USING_STDIN
// # define NDEBUG
// # define NCHECK
#include <cassert>
 
namespace Elaina {

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif
 
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair <int, int> pii;
    typedef pair <ll, ll> pll;
    
    template <class T> inline T fab(T x) { return x < 0 ? -x : x; }
    template <class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
    template <class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }
 
#ifndef USING_STDIN
    inline char freaGET() {
# define BUFFERSIZE 1 << 18
        static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
        return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
    }
# define CHARGET freaGET()
#else
# define CHARGET getchar()
#endif
    template <class T> inline T readret(T x) {
        x=0; int f = 0; char c;
        while((c = CHARGET) < '0' || '9' < c) if(c == '-') f = 1;
        for(x = (c^48); '0' <= (c = CHARGET) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
        return f ? -x : x;
    }
    template <class T> inline void readin(T& x) { x = readret(T(1)); }
    template <class T, class... Args> inline void readin(T& x, Args&... args) {
        readin(x), readin(args...);
    }

    template <class T> inline void writc(T x, char s='\n') {
        static int fwri_sta[55], fwri_ed = 0;
        if(x < 0) putchar('-'), x = -x;
        do fwri_sta[++fwri_ed] = x % 10, x /= 10; while(x);
        while(putchar(fwri_sta[fwri_ed--] ^ 48), fwri_ed);
        putchar(s);
    }

} using namespace Elaina;

const int maxn = 2.5e5;
const int inf = 0x3f3f3f3f;

int n, m;
vector <int> g[maxn + 5];
inline void add_edge(int u, int v) {
    g[u].push_back(v), g[v].push_back(u);
}

inline void input() {
    readin(n, m);
    int u, v;
    rep(i, 1, m) {
        readin(u, v);
        add_edge(u, v);
    }
}

namespace LCT {
    
    int son[maxn + 5][2], fa[maxn + 5];
    bool rev[maxn + 5];
    
    inline bool isroot(int x) {
        return son[fa[x]][0] != x && son[fa[x]][1] != x;
    }
    inline void connect(int x, int y, int d) {
        son[x][d] = y, fa[y] = x;
    }
    inline void rotate(int x) {
        int y = fa[x], z = fa[y], d = (son[y][1] == x);
        fa[x] = z; if(!isroot(y)) son[z][son[z][1] == y] = x;
        connect(y, son[x][d ^ 1], d), connect(x, y, d ^ 1);
    }
    inline void reverse(int x) {
        swap(son[x][0], son[x][1]), rev[x] ^= 1;
    }
    inline void pushdown(int x) {
        if(!rev[x]) return;
        if(son[x][0]) reverse(son[x][0]);
        if(son[x][1]) reverse(son[x][1]);
        rev[x] = 0;
    }
    inline void splay(int x) {
        static int stk[maxn + 5], ed = 0; int now = x;
        while(stk[++ed] = now, !isroot(now)) now = fa[now];
        while(ed) pushdown(stk[ed--]);
        int y, z;
        while(!isroot(x)) {
            y = fa[x], z = fa[y];
            if(!isroot(y))
                (son[z][1] == y) ^ (son[y][1] == x) ? rotate(x) : rotate(y);
            rotate(x);
        }
    }
    inline void access(int x) {
        for(int pre = 0; x; pre = x, x = fa[x])
            splay(x), son[x][1] = pre;
    }
    inline int findrt(int x) {
        access(x), splay(x), pushdown(x);
        while(son[x][0]) pushdown(x = son[x][0]);
        return splay(x), x;
    }
    inline void makert(int x) {
        access(x), splay(x), reverse(x);
    }
    inline bool link(int x, int y) {
        makert(x);
        if(findrt(y) == x) return false;
        fa[x] = y;
        return true;
    }
    inline bool cut(int x, int y) {
        makert(x);
        if(findrt(y) == x && fa[y] == x && !son[y][0]) {
            son[x][1] = fa[y] = 0;
            return true;
        }
        return false;
    }

} // using namespace saya;
int deg[maxn + 5];

namespace saya {

    int tag[maxn << 2 | 2];
    struct node {
        int mn, cnt;
        friend inline node operator + (const node& a, const node& b) {
            node ret = {min(a.mn, b.mn), 0}; 
            if(ret.mn == a.mn) ret.cnt += a.cnt;
            if(ret.mn == b.mn) ret.cnt += b.cnt;
            return ret;
        }
    } tre[maxn << 2 | 2];

#define ls (i << 1)
#define rs (i << 1 | 1)
#define mid ((l + r) >> 1)
#define _lhs ls, l, mid
#define _rhs rs, mid + 1, r

    void build(int i = 1, int l = 1, int r = n) {
        tre[i] = {0, r - l + 1}, tag[i] = 0;
        if(l == r) return;
        build(_lhs), build(_rhs);
    }
    inline void pushup(int i) { tre[i] = tre[ls] + tre[rs]; }
    inline void add(int i, int delta) { tre[i].mn += delta, tag[i] += delta; }
    inline void pushdown(int i) {
        if(!tag[i]) return;
        add(ls, tag[i]), add(rs, tag[i]), tag[i] = 0;
    }
    void modify(int ql, int qr, int delta, int i = 1, int l = 1, int r = n) {
        if(ql <= l && r <= qr) return add(i, delta);
        pushdown(i);
        if(ql <= mid) modify(ql, qr, delta, _lhs);
        if(mid < qr) modify(ql, qr, delta, _rhs);
        pushup(i);
    }
    node query(int ql, int qr, int i = 1, int l = 1, int r = n) {
        if(ql <= l && r <= qr) return tre[i];
        pushdown(i); node ret = {inf, 0};
        if(ql <= mid) ret = query(ql, qr, _lhs);
        if(mid < qr) ret = ret + query(ql, qr, _rhs);
        return ret;
    }
    void print(int i = 1, int l = 1, int r = n) {
        printf("node %d  [%d, %d] :> mn == %d, cnt == %d\n", i, l, r, tre[i].mn, tre[i].cnt);
        if(l == r) return;
        pushdown(i);
        print(_lhs), print(_rhs);
    }

#undef ls
#undef rs
#undef mid
#undef _lhs
#undef _rhs

} // using namespace saya;

inline void remove(int u, int r) {
    for(int v : g[u]) if(u <= v && v <= r) {
        if(LCT::cut(v, u)) --deg[v];
    }
}

inline void work() {
    int l = 1, r = 0;
    ll ans = 0;
    saya::build();
    while(r < n) {
        ++r; // extend the right endpos
        saya::modify(l, r, 1); // add the number of node
        for(int v : g[r]) if(l <= v && v <= r) {
            while(deg[v] == 2 || deg[r] == 2 || !LCT::link(r, v)) {
                remove(l++, r);
                if(l > v) break;
            }
            // add successfully
            if(l <= v) {
                ++deg[v], ++deg[r]; // increase the degree
                saya::modify(l, v, -1); // add the number of edge
            }
        }
        auto ret = saya::query(l, r);
        if(ret.mn == 1) ans += ret.cnt;
    }
    writc(ans);
}

signed main() {
    // freopen("txdy.in", "r", stdin);
    // freopen("txdy.out", "w", stdout);
    input();
    work();
    return 0;
}

肆、关键 の 地方 ¶

条件关于点的刻画,这是关键。另,移动右端点,维护左端点,又一次被用上。

上一篇:Jumping Monkey 2021CCPC网络赛重赛1011


下一篇:树上差分