Solution
假如要求的不是一棵树而是一个森林那就很好做,直接用一个双指针+LCT就可以对每个右端点维护出没有环的左端点。那么树这个限制怎么解决呢?树也就是连通块只有一个,而连通块数=点数-边数,用一个线段树维护这个东西就行了。
Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=200010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,a[1010][1010];LL ans=0;
int son[Maxn][2],fa[Maxn];bool rev[Maxn];
bool is(int x){return(son[fa[x]][0]!=x&&son[fa[x]][1]!=x);}
void Rev(int x)
{
rev[x]^=true;
swap(son[x][0],son[x][1]);
}
void down(int x)
{
if(rev[x])
{
int lc=son[x][0],rc=son[x][1];
if(lc)Rev(lc);
if(rc)Rev(rc);
rev[x]=false;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y],w=(son[y][0]==x);
son[y][w^1]=son[x][w];if(son[x][w])fa[son[x][w]]=y;
if(!is(y))son[z][son[z][1]==y]=x;fa[x]=z;
son[x][w]=y;fa[y]=x;
}
int sta[Maxn],top;
void update(int x)
{
top=0;
while(1)
{
sta[++top]=x;
if(is(x))break;
x=fa[x];
}
while(top)down(sta[top--]);
}
void splay(int x)
{
update(x);
while(!is(x))
{
int y=fa[x],z=fa[y];
if(!is(y))rotate((son[z][1]==y)==(son[y][1]==x)?y:x);
rotate(x);
}
}
void access(int x)
{
int last=0;
while(x)
{
splay(x);
son[x][1]=last;
last=x;x=fa[x];
}
}
void make_root(int x){access(x),splay(x),Rev(x);}
void link(int x,int y){/*printf("link %d %d\n",x,y);*/make_root(x),fa[x]=y;}
void cut(int x,int y){/*printf("cut %d %d\n",x,y);*/make_root(x),access(y),splay(y);son[y][0]=fa[x]=0;}
int find_root(int x){access(x),splay(x);while(son[x][0])x=son[x][0];return x;}
struct P{int mn,c;P(int _mn=0,int _c=0){mn=_mn,c=_c;}};
struct Seg{int l,r,lc,rc,tag;P t;}tr[Maxn<<1];
int tot=0;
void Add(int x,int v){tr[x].t.mn+=v;tr[x].tag+=v;}
P merge(P lc,P rc)
{
P re;
if(lc.mn==rc.mn)re.c=lc.c+rc.c;
else re.c=((lc.mn<rc.mn)?lc.c:rc.c);
re.mn=min(lc.mn,rc.mn);
return re;
}
void Down(int x)
{
int lc=tr[x].lc,rc=tr[x].rc,t=tr[x].tag;
if(t)
{
tr[x].tag=0;
Add(lc,t),Add(rc,t);
}
}
void build(int l,int r)
{
int x=++tot;
tr[x].l=l;tr[x].r=r;tr[x].tag=0;
if(l==r){tr[x].t=P(0,1);return;}
int mid=l+r>>1;
tr[x].lc=tot+1,build(l,mid);
tr[x].rc=tot+1,build(mid+1,r);
tr[x].t=merge(tr[tr[x].lc].t,tr[tr[x].rc].t);
}
void add(int x,int l,int r,int v)
{
if(l>r)return;
if(tr[x].l==l&&tr[x].r==r){Add(x,v);return;}
int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
Down(x);
if(r<=mid)add(lc,l,r,v);
else if(l>mid)add(rc,l,r,v);
else add(lc,l,mid,v),add(rc,mid+1,r,v);
tr[x].t=merge(tr[tr[x].lc].t,tr[tr[x].rc].t);
}
P query(int x,int l,int r)
{
if(tr[x].l==l&&tr[x].r==r)return tr[x].t;
int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
Down(x);
if(r<=mid)return query(lc,l,r);
if(l>mid)return query(rc,l,r);
return merge(query(lc,l,mid),query(rc,mid+1,r));
}
int px[Maxn],py[Maxn];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
bool in(int x,int y){return(x>0&&y>0&&x<=n&&y<=m);}
int l,r,tmp[5],lt;
bool check(int x,int y)
{
lt=0;
for(int i=0;i<4;i++)
{
int nx=x+dx[i],ny=y+dy[i];
if(!in(nx,ny))continue;
if(l<=a[nx][ny]&&a[nx][ny]<=r)
{
int t=find_root(a[nx][ny]);
for(int j=1;j<=lt;j++)
if(tmp[j]==t)
{
return false;
}
tmp[++lt]=t;
}
}
return true;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int x=read();
a[i][j]=x;
px[x]=i,py[x]=j;
}
build(1,n*m);add(1,1,1,1);
l=1;ans++;
for(r=2;r<=n*m;r++)
{
while(!check(px[r],py[r]))
{
for(int i=0;i<4;i++)
{
int nx=px[l]+dx[i],ny=py[l]+dy[i];
if(in(nx,ny)&&a[nx][ny]>l&&a[nx][ny]<r)
{
cut(l,a[nx][ny]);
}
}
l++;
}
add(1,l,r,1);
for(int i=0;i<4;i++)
{
int nx=px[r]+dx[i],ny=py[r]+dy[i];
if(!in(nx,ny))continue;
if(l<=a[nx][ny]&&a[nx][ny]<=r)
{
link(a[nx][ny],r);
add(1,l,a[nx][ny],-1);
}
}
P t=query(1,l,r);
if(t.mn==1)ans+=(LL)t.c;
}
printf("%lld",ans);
}