炸弹题解
这题有两种做法,一种tarjan,一种逆天DP
用lower_bound或upper查找i所在范围的左右边界对应下标
普通Tarjan+缩点
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10,mod=1e9+7;
int n,dfn[N],low[N],head2[N],num,cnt,head[N],belong[N];bool vis[N];
int x[N],R[N];
struct ac
{
int l=LONG_LONG_MAX,r=0,id;
// bool operator <(const ac &A)const
// {
// return x<A.x;
// }
}a[N],gd[N];
struct Edge
{
int to,next,from,id;
}edge[N*4],edge2[N*3];
int ge;
void add(int u,int v)
{
edge[++cnt].to=v;
edge[cnt].next=head[u];
edge[cnt].from=u;
head[u]=cnt;
}
int cnt2;
void add2(int u,int v)
{
edge2[++cnt2].to=v;
edge2[cnt2].next=head2[u];
edge2[cnt2].from=u;
head2[u]=cnt2;
}
stack <int> stk;
void tarjan(int now)
{
// cout<<1;
dfn[now]=low[now]=++num;
vis[now]=1;
stk.push(now);
for(int i=head[now];i;i=edge[i].next)
{
int to=edge[i].to;
if(!dfn[to])
{
tarjan(to);
low[now]=min(low[to],low[now]);
a[now].l=min(a[to].l,a[now].l);
a[now].r=max(a[to].r,a[now].r);
}else if(vis[to])
{
low[now]=min(dfn[to],low[now]);
a[now].l=min(a[to].l,a[now].l);
a[now].r=max(a[to].r,a[now].r);
}
}
if(low[now]==dfn[now])
{
int x;
ge++;
do
{
x=stk.top();
stk.pop();
vis[x]=0;
belong[x]=ge;
gd[ge].l=min(a[x].l,gd[ge].l);
gd[ge].r=max(a[x].r,gd[ge].r);
}while(now!=x);
}
}
bool flag[N];
int findl(int i,int val)
{
return lower_bound(x+1,x+1+n,val)-x;
}
int findr(int i,int val)
{
// return upper_bound(x+1,x+1+n,val)-x-1;
int s=lower_bound(x+1,x+1+n,val)-x;
if(x[s]!=val)
{
s--;
}
return s;
}
void dfs(int now)
{
flag[now]=1;
for(int i=head2[now];i;i=edge2[i].next)
{
int to=edge2[i].to;
// cout<<now<<" "<<to<<endl;
if(!flag[to])dfs(to);
gd[now].l=min(gd[to].l,gd[now].l);
gd[now].r=max(gd[to].r,gd[now].r);
// cout<<gd[now].l<<" "<<gd[now].r<<endl;
}
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x[i]>>R[i];
a[i].id=i;
}
for(int i=1;i<=n;i++)
{
a[i].l=findl(i,x[i]-R[i]);
a[i].r=findr(i,x[i]+R[i]);
}//这里要提前全部更新出来,不能套在下面循环中,MD调了半天
for(int i=1;i<=n;i++)
{
for(int j=a[i].l;j<=a[i].r;j++)
{
if(i==j)continue;
add(i,j);
j=a[j].r;//防止j一个一个加炸内存,很巧妙,i连j以后,只用考虑j右边界以外的情况,因为i循环到j时会连j内的情况
}
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])tarjan(i);
}
// for(int i=1;i<=n;i++)cout<<belong[i]<<" ";
// for(int i=1;i<=n;i++)
// {
for(int j=1;j<=cnt;j++)
// for(int j=head[i];j;j=edge[j].next)
{
if(belong[edge[j].from]!=belong[edge[j].to])
{
// cout<<belong[edge[j].from]<<" "<<belong[edge[j].to]<<endl;
add2(belong[edge[j].from],belong[edge[j].to]);
}
}
// }
long long ans=0;
for(int i=1;i<=ge;i++)if(!flag[i])dfs(i);
for(int i=1;i<=n;i++)
{
// cout<<gd[belong[i]].l<<" "<<gd[belong[i]].r<<endl;
ans=(ans+(gd[belong[i]].r-gd[belong[i]].l+1)*a[i].id%mod)%mod;
}
cout<<ans;
return 0;
}
/*
4
1 1
5 1
6 5
15 15
*/
线段树优化
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mid ((l + r) >> 1)
#define lid rt<<1
#define rid (rt<<1|1)
const int N = 5e5+10, M=2e6+10, mod =1e9 + 7;
struct node {
int l,r;
}a[M];
ll x[N], r[N];
int dfn[M], low[M], bel[M];
int id[M], Left[M], Right[M];
int n, nd, cnt, idx;ll ans;
stack<int> s;
vector<int> e[M], G[M];
bool vis[M];
void bt(int rt,int l,int r)
{
a[rt].l=l;a[rt].r=r;
nd=max(nd,rt);
if(l==r)
{
id[l]=rt;
return;
}
bt(lid,l,mid);
bt(rid,mid+1,r);
e[rt].push_back(lid);e[rt].push_back(rid);
}
void update(int rt,int l,int r,int L,int R,int id)
{
if(L<=l&&r<=R)
{
if(rt==id)return;
e[id].push_back(rt);
return;
}
if(L<=mid)update(lid,l,mid,L,R,id);
if(R>mid)update(rid,mid+1,r,L,R,id);
}
void tarjan(int x) {
dfn[x] = low[x] = ++ cnt;
s.push(x), vis[x] = 1;
for (int i = 0; i < e[x].size(); i ++) {
int y = e[x][i];
if (!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if (vis[y]) low[x] = min(low[x], dfn[y]);
}
if (dfn[x] == low[x]) {
idx ++;
int v;
do {
v = s.top(); s.pop();
vis[v] = 0;
bel[v] = idx;
Left[idx] = min(Left[idx], a[v].l);
Right[idx] = max(Right[idx], a[v].r);
} while (v != x);
}
}
void dfs(int now)
{
vis[now]=1;
for(int i=0;i<G[now].size();i++)
{
int to=G[now][i];
if (vis[to]) {
Left[now] = min(Left[now], Left[to]);
Right[now] = max(Right[now], Right[to]);
continue;
}
dfs(to);
Left[now] = min(Left[now], Left[to]);
Right[now] = max(Right[now], Right[to]);
}
}
ll query(int x)
{
int u=bel[id[x]];
return Right[u]-Left[u]+1;
}
int main()
{
scanf("%d", &n);
for(int i=1;i<=n;i++)scanf("%lld %lld", &x[i], &r[i]);
memset(Left, 0x3f, sizeof Left);
bt(1,1,n);
// for(int i=1;i<=n;i++)cout<<id[i]<<" ";
for(int i=1;i<=n;i++)
{
int L=lower_bound(x + 1, x + n + 1, x[i] - r[i]) - x;
int R=upper_bound(x + 1, x + n + 1, x[i] + r[i]) - x - 1;
update(1,1,n,L,R,id[i]);
a[id[i]].l=L;a[id[i]].r=R;
}
tarjan(1);
for (int i = 1; i <= nd; i ++) {
for (int j = 0; j < e[i].size(); j ++) {
int u = e[i][j];
if (bel[u]!= bel[i])
G[bel[i]].push_back(bel[u]);
}
}
for(int i=1;i<=idx;i++)
{
sort(G[i].begin(),G[i].end());
unique(G[i].begin(),G[i].end());
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=idx;i++)
{
if(!vis[i])dfs(i);
}
for (int i=1;i<=n;i++)
ans = (ans + 1ll*query(i)*i)%mod;
printf("%lld", ans);
return 0;
}
逆天做法
一个DP,先从左往右一遍,在从右往左一遍,两个数组l,r就是左右边界
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const long long N=500005,mod=1e9+7;
int n;
struct ac
{
int x,r,l,id;
bool operator <(const ac &A)const
{
return x<A.x;
}
}a[N];
int f[N],sum[N];
int l[N],r[N];int ans;
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].r;
l[i]=r[i]=i;
}
// sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
{
while(a[i].x-a[i].r<=a[l[i]-1].x&&l[i]>1)
{
a[i].r=max(a[i].r,a[l[i]-1].r-(a[i].x-a[l[i]-1].x));//更新l时更新一下R,意思为xi+ri->xj+rj
l[i]=l[l[i]-1];
}
}
for(int i=n;i>=1;i--)
{
while(a[i].x+a[i].r>=a[r[i]+1].x&&r[i]<n)
{
l[i]=min(l[i],l[r[i]+1]);
r[i]=r[r[i]+1];
}
}
for(int i=1;i<=n;i++)
{
// cout<<a[i].id<<" "<<f[i]<<" "<<endl;
ans+=(r[i]-l[i]+1)*i;
}
cout<<(long long)ans%mod;
return 0;
}