没时间去编了。
比较经典地,移动右端点指针,维护合法左端点。
左端点合法,指将 \([l,r]\) 的点都保留下来不成环且每个点度数小于等于 \(2\).
然后我们需要维护合法的左端点,一个左端点合法,当且仅当将 \([l,r]\) 的点都保留下来之后,有 \(|V_l|-|E_l|=1\).
综上,我们只需要处理三个部分:
第一个很好维护,第二个可以考虑使用 \(\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; }
条件关于点的刻画,这是关键。另,移动右端点,维护左端点,又一次被用上。