或曰:“笑长天下第一!”,OID 喜得合不拢嘴:“哈哈哈哈哈哈……”
OneInDark 是天下第一的。
OneInDark 给了你一个
n
n
n 个点
m
m
m 条边的无向图(无重边自环),点标号为
1
∼
n
1 ∼ n
1∼n。祂想要考考你,有多少对整数对
(
l
,
r
)
(l, r)
(l,r) 满足:
1
≤
l
≤
r
≤
n
1 ≤ l ≤ r ≤ n
1≤l≤r≤n
如果把区间
[
l
,
r
]
[l, r]
[l,r] 内的点以及它们之间的边保留下来,其他的点和边都扔掉,那么留下来的这张图恰好是一条链。
注:链必须是连通的图。特别地,一个点的图也是链。
n
,
m
≤
2.5
×
1
0
5
n,m\leq 2.5\times10^5
n,m≤2.5×105
既然它是一条链,就没有度数大于 2 的点,同时又无环。
如果无环,那么就是森林(重点:一棵树也是森林),森林成为连通图只需要满足一个条件:点数-边数=1。同时由于森林满足 点数-边数 ≥ 1,所以该条件等价于(点数-边数)取得最小值 1 。
于是,我们认定链要(先后)满足三个条件:
如果固定区间右端点
R
R
R,我们会发现只满足前两个条件的区间左端点
L
L
L 是连续的,从某个位置一直到
R
R
R。同时若区间
[
L
,
R
]
[L,R]
[L,R] 满足前两个条件,那么区间
[
L
,
R
−
1
]
[L,R-1]
[L,R−1] (如果非空的话)也一定满足前两个条件。所以我们用双指针(曰Two Pointers,曰尺取(?)……)配合LCT(判断环),求出对于每个右端点的最远合法——仅对前两个条件合法——的左端点。
这时候要满足第三个条件,就很简单了。我们用线段树,维护左端点在每个位置时的(点数-边数)最小值及其个数。
时间复杂度
O
(
(
n
+
m
)
log
n
)
O((n+m)\log n)
O((n+m)logn) 。
做 LCT 要谨记一点:平衡树上凡是访问过的链,都得Splay旋到根,相当于对Splay进行一次“训练”,不然复杂度不对。
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 250005
#define LL long long
#define ULL unsigned long long
#define UI unsigned int
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define eps (1e-4)
#define BI bitset<1002>
#pragma GCC optimize(2)
LL read() {
LL f=1,x=0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f*x;
}
void putpos(LL x) {
if(!x) return ;
putpos(x/10); putchar('0'+(x%10));
}
void putnum(LL x) {
if(!x) putchar('0');
else if(x < 0) putchar('-'),putpos(-x);
else putpos(x);
}
void AIput(LL x,char c) {putnum(x);putchar(c);}
int n,m,s,o,k;
// -------------------------------------START >>>
struct tr{
int s[2],fa,lz;
tr(){s[0]=s[1]=fa=lz=0;}
}tre[MAXN];
int isroot(int x) {return tre[tre[x].fa].s[0] != x && tre[tre[x].fa].s[1] != x;}
int whichson(int x) {return tre[tre[x].fa].s[1] == x;}
int update(int x) {
if(!x) return x;
tre[tre[x].s[0]].fa = tre[tre[x].s[1]].fa = x;
tre[0].fa = 0;
return x;
}
void pushdown(int x) {
if(tre[x].lz) {
swap(tre[x].s[0],tre[x].s[1]);
tre[tre[x].s[0]].lz ^= 1;
tre[tre[x].s[1]].lz ^= 1;
tre[x].lz = 0;
}return ;
}
void rotat(int x) {
int y = tre[x].fa,z = tre[y].fa;
int d = whichson(x),d2 = whichson(y),d3 = isroot(y);
tre[y].s[d] = tre[x].s[d^1];
tre[x].s[d^1] = y; tre[x].fa = z;
tre[tre[y].s[d]].fa = y; tre[y].fa = x;
if(!d3) tre[z].s[d2] = x; tre[0].fa = 0;
return ;
}
void pushup(int x) {
static int st[MAXN],tp;
st[tp = 1] = x;
while(!isroot(x)) st[++ tp] = tre[x].fa,x = tre[x].fa;
while(tp > 0) pushdown(st[tp --]);
return ;
}
int splay(int x) {
pushup(x);
while(!isroot(x)) {
int y = tre[x].fa;
if(!isroot(y) && whichson(x) == whichson(y)) rotat(y);
rotat(x);
}return x;
}
// ---------------------------SPLAY
int Access(int x) {
int p = 0,xx = x;
while(x) {
splay(x);
tre[x].s[1] = p;
p = update(x);
x = tre[x].fa;
}return splay(xx);
}
void Makeroot(int x) {Access(x);tre[x].lz ^= 1;}
int Findroot(int x) {Access(x);while(tre[x].s[0])x=tre[x].s[0];return splay(x);}
void Cut(int x,int y) {Makeroot(x);Access(y);tre[y].s[0]=tre[x].fa=0;update(y);}
void Link(int x,int y) {Makeroot(x);tre[x].fa = y;return ;}
// ---------------------------LCT
int hd[MAXN],v[MAXN<<1],nx[MAXN<<1],cne;
int ind[MAXN],cnd;
map<int,bool> mp[MAXN];
void ins(int x,int y) {
nx[++ cne] = hd[x]; v[cne] = y; hd[x] = cne;
}
void del(int x) {
for(int i = hd[x];i;i = nx[i]) {
if(mp[min(x,v[i])][max(x,v[i])]) {
Cut(x,v[i]);
mp[min(x,v[i])][max(x,v[i])] = 0;
ind[x] --; ind[v[i]] --;
if(ind[x] == 2) cnd --;
if(ind[v[i]] == 2) cnd --;
}
}return ;
}
int pl[MAXN];
struct it{
int nm,ct;
it(){nm=0x3f3f3f3f;ct=0;}
}tr[MAXN<<2];
int lz[MAXN<<2],M;
it merg(it a,it b) {
if(a.nm > b.nm) swap(a,b);
if(a.nm == b.nm) a.ct += b.ct;
return a;
}
it operator + (it a,int b) {a.nm += b; return a;}
void maketree(int n) {
M=1;while(M<n+2)M<<=1;
for(int i = 1;i <= n;i ++) {
tr[M+i].nm = 0;tr[M+i].ct = 1;
}
for(int i = M-1;i > 0;i --) {
tr[i] = merg(tr[i<<1],tr[i<<1|1]);
}return ;
}
void addtree(int l,int r,int y) {
int s = M+l-1,t = M+r+1;
while(s || t) {
if(s<M) tr[s] = merg(tr[s<<1],tr[s<<1|1]) + lz[s];
if(t<M) tr[t] = merg(tr[t<<1],tr[t<<1|1]) + lz[t];
if((s>>1) ^ (t>>1)) {
if(!(s&1)) tr[s^1]=tr[s^1]+y,lz[s^1]+=y;
if(t & 1) tr[t^1]=tr[t^1]+y,lz[t^1]+=y;
}s >>= 1;t >>= 1;
}return ;
}
it findtree(int l,int r) {
it ls = it(),rs = it();
if(l > r) return ls;
int s = M+l-1,t = M+r+1;
while(s || t) {
ls = ls + lz[s];
rs = rs + lz[t];
if((s>>1) ^ (t>>1)) {
if(!(s&1)) ls = merg(ls,tr[s^1]);
if(t & 1) rs = merg(rs,tr[t^1]);
}s >>= 1;t >>= 1;
}return merg(ls,rs);
}
int main() {
freopen("txdy.in","r",stdin);
freopen("txdy.out","w",stdout);
n = read();m = read();
for(int i = 1;i <= m;i ++) {
s = read();o = read();
ins(s,o); ins(o,s);
}
int st = 1;
maketree(n);
LL ans = 0;
for(int i = 1;i <= n;i ++) {
addtree(1,i,1);
for(int j = hd[i];j;j = nx[j]) {
s = i; o = v[j];
while(o >= st && o <= i && Findroot(o) == Findroot(s)) {
del(st ++);
}
if(o >= st && o <= i) {
Link(s,o);
mp[min(s,o)][max(s,o)] = 1;
ind[s] ++; ind[o] ++;
if(ind[s] == 3) cnd ++;
if(ind[o] == 3) cnd ++;
}
if(v[j] < i) {
addtree(1,v[j],-1);
}
}
while(cnd > 0) del(st ++);
pl[i] = st;
it as = findtree(pl[i],i);
if(as.nm == 1) ans += as.ct;
}
AIput(ans,'\n');
return 0;
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章