题解:CF2283F Minimize the Graph
题意
给定一张有向图,顶点编号为
对于所有
现在要构造一张新的有向图,使得对于任意两个不同顶点
求新图最少需要多少条边。
解析
考虑原图中的一条边
这条边能被删除,当且仅当存在一条从
定义
也就是说,
对于边
即
原图中存在路径
其中边
所有被删除的边都能用保留下来的边形成替代路径,可达性仍然保持不变。
综上,若
若边
由于所有边都指向编号更大的点,所以:
路径上
有
因为存在边
若边
综上,若边可以删除,则一定有
对于每个
其中满足
所以最少边数为
问题转化为对每个
依次处理给定的区间
对于区间中还没有被赋值的位置
使用并查集维护下一个还没有被赋值的位置,每个位置最多被赋值一次,复杂度为:
对于每个
这是一个二维偏序问题,可以离线处理:
-
将所有位置
x 按照mi_x 从小到大排序,存入pos; -
将所有节点
i 按照r_i 从小到大排序,存入id; -
对于当前的
i ,把所有满足mi[pos[p]]<=r[i]的位置pos[p]加入树状数组,统计区间[l_i,r_i] 中已经加入的位置个数。
当我们按照
因此已经加入树状数组的位置以后也一定满足条件,不需要删除,只需要不断加入新的位置。
时间复杂度
AC Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define int long long
#define ri register int
#define rd(n) n=read()
const int N=300005;
inline int read(void)
{
ri res=0,f=0;
register char c=getchar();
while(c<'0'||c>'9'){f^=(c=='-');c=getchar();}
while(c>='0'&&c<='9'){res=(res<<3)+(res<<1)+(c^48);c=getchar();}
return f?-res:res;
}
inline void fprint(int n)
{
if(n<0){putchar('-');n=-n;}
if(n>9) fprint(n/10);
putchar(n%10+'0');
}
inline void print(int n)
{
fprint(n);
putchar('\n');
}
int T,n,m,x,ans;
int l[N],r[N],mi[N],f[N],dat[N];
inline int find(int x)//DSU
{
while(f[x]!=x)
{
f[x]=f[f[x]];
x=f[x];
}
return x;
}
#define lowbit(x) ((x)&-(x))
inline void add(int x)//BIT
{
for(;x<=n;x+=lowbit(x))
++dat[x];
}
inline int query(int x)
{
ri ret=0;
for(;x;x-=lowbit(x))
ret+=dat[x];
return ret;
}
inline bool cmp1(int a,int b)
{
if(mi[a]!=mi[b]) return mi[a]<mi[b];
return a<b;
}
inline bool cmp2(int a,int b)
{
if(r[a]!=r[b]) return r[a]<r[b];
return a<b;
}
vector<int>pos,id;
inline void Pre_work(void)
{
for(ri i=0;i<=n+1;++i)
{
f[i]=i;
mi[i]=n+1;
dat[i]=0;
}
pos.clear();
id.clear();
}
inline void solve(void)
{
rd(n);
Pre_work();
ans=0;
for(ri i=2;i<=n;++i)
{
rd(l[i]),rd(r[i]);
x=find(l[i]);
while(x<=r[i])
{
mi[x]=i;
f[x]=find(x+1);
x=find(x);
}
}
pos.push_back(1);
for(ri i=2;i<=n;++i)
pos.push_back((int)i),id.push_back((int)i);
sort(pos.begin(),pos.end(),cmp1);
sort(id.begin(),id.end(),cmp2);
ri p=0,mm=id.size(),nn=pos.size();
for(ri i=0;i<mm;++i)
{
ri d=id[i];
while(p<nn&&mi[pos[p]]<=r[d]) add(pos[p]),++p;
ans+=(r[d]-l[d]+1)-(query(r[d])-query(l[d]-1));
}
print(ans);
}
signed main(void)
{
rd(T);
while(T--) solve();
return 0;
}