P5025 [SNOI2017]炸弹

 这题非常显然可以用线段树+拓排水过去,但是稍微看了下第一名的时间,非常显然有更优的建法。

发现轰炸到的区间一定是连续的,于是每个点只要让左右能炸到它的最近的点向它连边就行了,单调栈解决。

然后缩点加拓排。交上去发现还是慢了不少,发现前几都用基数排序优化了下,不过本人比较懒,还是鸽了。

代码如下。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const ll N=5e5+10,mod=1e9+7;
struct node{ll l,r,x,d;}a[N];queue<int> q;
ll n,m,ans,t,num,tot,tc,cnt,top,d[N],du[N],ls[N],rs[N],b[N],c[N],s[N],stack[N],ins[N],dfn[N],low[N],head[N],ver[N*2],next[N*2],hc[N],vc[N*2],nc[N*2];
inline ll read(){
    ll 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<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
inline bool operator<(const node&x,const node&y){
    return x.x<y.x;
}
inline void add(int x,int y){
    ver[++tot]=y;next[tot]=head[x];head[x]=tot;
}
inline void add_c(int x,int y){
    vc[++tc]=y;nc[tc]=hc[x];hc[x]=tc;
}
inline void tarjan(int x){
    dfn[x]=low[x]=++num;
    stack[++top]=x;ins[x]=1;
    for(int i=head[x];i;i=next[i]){
        if(!dfn[ver[i]]) tarjan(ver[i]),low[x]=min(low[x],low[ver[i]]);
        else if(ins[ver[i]]) low[x]=min(low[x],dfn[ver[i]]);
    }
    if(dfn[x]==low[x]){
        ++cnt;int y;
        do{
            y=stack[top--];c[y]=cnt;ins[y]=0;
            ls[cnt]=min(ls[cnt],1ll*y);
            rs[cnt]=max(rs[cnt],1ll*y);
        }while(x!=y);
    }
}
int main(){
    m=n=read();for(int i=1;i<=n;i++) ls[i]=n+1,b[i]=a[i].x=read(),a[i].d=read();
    sort(a+1,a+1+n);sort(b+1,b+1+m);m=unique(b+1,b+1+m)-(b+1);
    for(int i=1;i<=n;i++){
        a[i].l=lower_bound(b+1,b+1+m,a[i].x-a[i].d)-b;
        a[i].r=upper_bound(b+1,b+1+m,a[i].x+a[i].d)-(b+1);
        a[i].x=lower_bound(b+1,b+1+m,a[i].x)-b;
    }
    for(int i=1;i<=n;i++){
        while(top&&a[s[top]].r<a[i].x) top--;
        if(top&&a[s[top]].r>=a[i].x) add(s[top],i);
        while(top&&a[s[top]].r<=a[i].r) top--;
        s[++top]=i;
    }
    top=0;
    for(int i=n;i>=1;i--){
        while(top&&a[s[top]].l>a[i].x) top--;
        if(top&&a[s[top]].l<=a[i].x) add(s[top],i);
        while(top&&a[s[top]].l>=a[i].l) top--;
        s[++top]=i;
    }
    top=0;
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int x=1;x<=n;x++){
        for(int i=head[x];i;i=next[i]){
            int y=ver[i];
            if(c[x]==c[y]) continue;
            add_c(c[x],c[y]);du[c[y]]++;
        }
    }
    for(int i=1;i<=cnt;i++) if(!du[i]) q.push(i);
    while(q.size()){
        int x=q.front();q.pop();d[++t]=x;
        for(int i=hc[x];i;i=nc[i]){
            if(!(--du[vc[i]])) q.push(vc[i]);
        }
    }
    for(int j=t;j>=1;j--){
        int x=d[j];
        for(int i=hc[x];i;i=nc[i]){
            int y=vc[i];
            ls[x]=min(ls[x],ls[y]);
            rs[x]=max(rs[x],rs[y]);
        }
    }
    for(int i=1;i<=n;i++) ans=(ans+1ll*i*(rs[c[i]]-ls[c[i]]+1)%mod)%mod;
    return printf("%lld\n",ans),0;
}

 

上一篇:LOJ2257 SNOI2017 遗失的答案 容斥、高维前缀和


下一篇:String.intern() 方法__jdk1.6与jdk1.7/jdk1.8的不同