壹、关于题目 ¶
没时间去编了。
贰、关于题解 ¶
比较经典地,移动右端点指针,维护合法左端点。
左端点合法,指将 \([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;
}
肆、关键 の 地方 ¶
条件关于点的刻画,这是关键。另,移动右端点,维护左端点,又一次被用上。