这道题如果强行爆搜的话时间复杂度应该是N^2的,所以我们要考虑怎么优化。
这就是这道题的主要方法:给一个区间连边而不是区间里的每一个点,这样的话时间复杂度就会降成log的复杂度,变成了nlogn,这个复杂度在我们可接受范围之内。
具体的来讲就是已n+1为线段树的根的编号,开始建立新的节点,如果搜到了叶子节点,它的编号就变成了自己原来的编号,每当要向区间连边时,就从这个区间上的点连接包含你要连的区间的节点。
建树的代码如下:
void build(int &t,int l,int r)
{
if(l==r)
{
lc[l]=rc[l]=t=l;
return;
}
t=++ndnum;
lc[t]=l;
rc[t]=r;
int mid=(l+r)/2;
build(lson[t],l,mid);
build(rson[t],mid+1,r);
add(t,lson[t]);
add(t,rson[t]);//连边
}
然后就是连接区间了:
void connect(int id,int t,int l,int r)
{
if(l<=lc[id]&&rc[id]<=r) { add(t,id); return; }
int mid=(lc[id]+rc[id])/2;
if(l<=mid) connect(lson[id],t,l,r);
if(mid<r) connect(rson[id],t,l,r);
return;
}
这道题连完点之后再用Tarjan缩点成一个DAG然后建一个反图,跑一边拓扑,一边跑一遍跟新答案。
完整代码如下:
#include
#define LL long long
#define int long long
using namespace std;
const int maxn = 800010 , mod = 1000000007 , inf = 1000000007;
int n,ndnum=0,root;
int lson[maxn*4],rson[maxn*4],rc[maxn*4],lc[maxn*4];
int head[maxn*4],sc[maxn*4],cnt=0;
LL zha[maxn*4],pos[maxn*4];
int dfn[maxn*4],sta[maxn*4],top=0,low[maxn*4],tot=0;
int b[maxn*4],tim=0,out[maxn*4];
LL ans=0;
bool vis[maxn*4];
struct edge
{
int to,pre;
}e[maxn*4];
struct scc1
{
int to,pre;
}scc[maxn*4];
struct point
{
int size;
int l,r;
}po[maxn*4];
template<class T> void read(T &re)
{
re=0;
T sign=1;
char tmp;
while((tmp=getchar())&&(tmp<'0'||tmp>'9')) if(tmp=='-') sign=-1;
re=tmp-'0';
while((tmp=getchar())&&(tmp>='0'&&tmp<='9')) re=(re<<3)+(re<<1)+(tmp-'0');
re*=sign;
}
inline void add(int x,int y)
{
e[++cnt].pre=head[x];
e[cnt].to=y;
head[x]=cnt;
}
inline void add_scc(int x,int y)
{
scc[++cnt].pre=sc[x];
scc[cnt].to=y;
sc[x]=cnt;
}
void build(int &t,int l,int r)
{
if(l==r)
{
lc[l]=rc[l]=t=l;
return;
}
t=++ndnum;
lc[t]=l;
rc[t]=r;
int mid=(l+r)/2;
build(lson[t],l,mid);
build(rson[t],mid+1,r);
add(t,lson[t]);
add(t,rson[t]);
}
void connect(int id,int t,int l,int r)
{
if(l<=lc[id]&&rc[id]<=r) { add(t,id); return; }
int mid=(lc[id]+rc[id])/2;
if(l<=mid) connect(lson[id],t,l,r);
if(mid<r) connect(rson[id],t,l,r);
return;
}
int lower_l(int x)
{
int l=1,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(pos[mid]>=x)
r=mid-1;
else
l=mid+1;
}
return l;
}
int lower_r(int x)
{
int l=1,r=n,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(pos[mid]>x)
r=mid-1;
else
l=mid+1;
}
return l;
}
void Tarjan(int x)
{
dfn[x]=low[x]=++tim;
vis[x]=1;
sta[++top]=x;
for(int i=head[x];i;i=e[i].pre)
{
int v=e[i].to;
if(!dfn[v])
{
Tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(vis[v])
low[x]=min(low[x],dfn[v]);
}
if(dfn[x]==low[x])
{
tot++;int y;
do
{
y=sta[top--];
b[y]=tot;
vis[y]=0;
if(y>=1&&y<=n)
{
po[tot].l=min(lc[y],po[tot].l);
po[tot].r=max(rc[y],po[tot].r);
}
}
while(x!=y);
}
}
queue<int >q;
void top_sort()
{
for(int i=1;i<=tot;++i)
if(!out[i]) q.push(i);
while(q.size())
{
int x=q.front();q.pop();
po[x].size=(LL)(po[x].r-po[x].l+1);
for(int i=sc[x];i;i=scc[i].pre)
{
int v=scc[i].to;
out[v]--;
if(!out[v]) q.push(v);
po[v].l=min(po[v].l,po[x].l);
po[v].r=max(po[v].r,po[x].r);
}
}
}
signed main()
{
read(n);
ndnum=n;
build(root,1,n);
for(int i=1;i<=ndnum;++i) po[i].l=inf,po[i].r=-inf;
for(int i=1;i<=n;++i) read(pos[i]),read(zha[i]);
for(int i=1;i<=n;++i)
{
int l=lower_l(pos[i]-zha[i]),r=lower_r(pos[i]+zha[i])-1;
connect(root,i,l,r);
}
for(int i=1;i<=ndnum;++i)
if(!dfn[i])
Tarjan(i);
cnt=0;
for(int x=1;x<=ndnum;++x)
{
for(int i=head[x];i;i=e[i].pre)
{
int v=e[i].to;
if(b[x]!=b[v])
{
add_scc(b[v],b[x]);
out[b[x]]++;
}
}
}
top_sort();
int ans=0;
for(int i=1;i<=n;++i)
ans=(ans+(LL)i*po[b[i]].size)%mod;
printf("%lld",ans);
return 0;
}