题目:BZOJ5017.
题目大意:给定两个长度为
n
n
n的序列
x
i
,
r
i
x_i,r_i
xi,ri,若
x
i
−
r
i
≤
x
j
≤
x
i
+
r
i
x_i-r_i\leq x_j\leq x_i+r_i
xi−ri≤xj≤xi+ri,则称
i
i
i能影响
j
j
j;若
a
a
a能影响
b
b
b,
b
b
b能影响
c
c
c,则
a
a
a能影响
c
c
c.设
f
(
i
)
f(i)
f(i)表示
i
i
i能影响的数的个数,求:
a
n
s
=
∑
i
=
1
n
i
∗
f
(
i
)
    
(
m
o
d
  
1
0
9
+
7
)
ans=\sum_{i=1}^{n}i*f(i)\,\,\,\,(mod\,\,10^9+7)
ans=i=1∑ni∗f(i)(mod109+7)
1 ≤ n ≤ 5 ∗ 1 0 5 , x i − 1 < x i 1\leq n\leq 5*10^5,x_{i-1}<x_i 1≤n≤5∗105,xi−1<xi.
首先,按照套路,我们把建 n n n个点,将第 i i i个点向第 i i i个点能够影响到的所有点连一条边.
然后我们就知道了,在这张图上 i i i能够到达的所有点都是能被 i i i影响的,所以我们需要统计 i i i能够到达的点的数目.
由于这张图可能有环出现,所以我们先跑个强连通分量缩点,缩完之后再统计就变成了在DAG上统计一个点能够到达的点的数目,可以很容易解决.
不幸的是,这个做法建图是 O ( n 2 ) O(n^2) O(n2)的,统计是 O ( n 2 ) O(n^2) O(n2)的.
不过没关系,容易发现每一个点连向的所有点必定是编号在一个连续区间内的点,所以线段树优化建图即可将建图复杂度降为 O ( n log n ) O(n\log n) O(nlogn).
不过,统计DAG上一个点能够到达的点的数目好像怎么写都是 O ( n 2 ) O(n^2) O(n2)的(最多用bitset优化成 O ( n 2 32 ) O(\frac{n^2}{32}) O(32n2),然而要跑 50 W 50W 50W的数据还是算了吧…).
当然这道题是一定可以做的,所以我们要分析一个点可以到达点的性质.
容易发现一个点可以到达的所有点必然也是在一个连续区间内的,所以我们只要用DP求出这个区间的左右端点(可以到达节点的编号最大最小值),这个显然是可以做到现行复杂度解决的.
时间复杂度 O ( n log n ) O(n\log n) O(nlogn).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=500000,mod=1000000007,C=15,INF=(1<<30)-1;
int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int sub(int a,int b){return a-b<0?a-b+mod:a-b;}
int mul(int a,int b){return (LL)a*b%mod;}
void sadd(int &a,int b){a=add(a,b);}
void ssub(int &a,int b){a=sub(a,b);}
void smul(int &a,int b){a=mul(a,b);}
int n;
LL a[N+9],r[N+9];
int Lower(LL x){
int l=1,r=n,mid=l+r>>1;
for (;l<r;mid=l+r>>1)
x>a[mid]?l=mid+1:r=mid;
return l;
}
int Upper(LL x){
int l=1,r=n,mid=l+r+1>>1;
for (;l<r;mid=l+r+1>>1)
x<a[mid]?r=mid-1:l=mid;
return l;
}
struct side{
int y,next;
}e[N*C*4+9];
int lin[2][N*4+9],cs;
void Ins(int id,int x,int y){e[++cs].y=y;e[cs].next=lin[id][x];lin[id][x]=cs;}
int tr[N*4+9],cn;
void Build(int L,int R,int k){
if (L==R) {tr[k]=L;return;}
tr[k]=++cn;
int mid=L+R>>1;
Build(L,mid,k<<1);Build(mid+1,R,k<<1|1);
Ins(0,tr[k],tr[k<<1]);Ins(0,tr[k],tr[k<<1|1]);
}
void Link(int u,int L,int R,int l,int r,int k){
if (L==l&&R==r) {Ins(0,u,tr[k]);return;}
int mid=l+r>>1;
if (R<=mid) Link(u,L,R,l,mid,k<<1);
else if (L>mid) Link(u,L,R,mid+1,r,k<<1|1);
else Link(u,L,mid,l,mid,k<<1),Link(u,mid+1,R,mid+1,r,k<<1|1);
}
int dfn[N*4+9],low[N*4+9],vis[N*4+9],co;
stack<int>sta;
int bel[N*4+9],cnt;
vector<int>scc[N*4+9];
void Tarjan(int k){
dfn[k]=low[k]=++co;
vis[k]=1;sta.push(k);
for (int i=lin[0][k];i;i=e[i].next)
if (!dfn[e[i].y]){
Tarjan(e[i].y);
low[k]=min(low[k],low[e[i].y]);
}else if (vis[e[i].y]) low[k]=min(low[k],dfn[e[i].y]);
if (dfn[k]^low[k]) return;
++cnt;
while (vis[k]){
int t=sta.top();sta.pop();
vis[t]=0;
scc[bel[t]=cnt].push_back(t);
}
}
int mx[N*4+9],mn[N*4+9],ans;
void Contract(){
for (int i=1;i<=cn;++i)
if (!dfn[i]) Tarjan(i);
for (int i=1;i<=cn;++i)
for (int j=lin[0][i];j;j=e[j].next)
if (bel[i]^bel[e[j].y]) Ins(1,bel[i],bel[e[j].y]);
}
void Dfs_dp(int k){
if (vis[k]) return;
vis[k]=1;
for (int i=lin[1][k];i;i=e[i].next){
Dfs_dp(e[i].y);
mx[k]=max(mx[k],mx[e[i].y]);
mn[k]=min(mn[k],mn[e[i].y]);
}
}
Abigail into(){
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%lld%lld",&a[i],&r[i]);
}
Abigail work(){
cn=n;
Build(1,n,1);
for (int i=1;i<=n;++i)
Link(i,Lower(a[i]-r[i]),Upper(a[i]+r[i]),1,n,1);
Contract();
for (int i=1;i<=cnt;++i) mx[i]=-INF,mn[i]=INF,vis[i]=0;
for (int i=1;i<=n;++i){
mx[bel[i]]=max(mx[bel[i]],Upper(a[i]+r[i]));
mn[bel[i]]=min(mn[bel[i]],Lower(a[i]-r[i]));
}
for (int i=1;i<=n;++i){
Dfs_dp(bel[i]);
sadd(ans,mul(i,mx[bel[i]]-mn[bel[i]]+1));
}
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}
本文详细解析了BZOJ5017题的解题思路,通过建立图模型并运用强连通分量缩点和DP算法,实现了在O(nlogn)的时间复杂度下求解序列中每个元素影响范围的问题。
1083

被折叠的 条评论
为什么被折叠?



