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; }