• BZOJ5017 [Snoi2017]炸弹[线段树优化建边+scc缩点+DAG上DP/线性递推]


    方法一:

    朴素思路:果断建图,每次二分出一个区间然后要向这个区间每个点连有向边,然后一个环的话是可以互相引爆的,缩点之后就是一个DAG,求每个点出发有多少可达点。

    然后注意两个问题:

    • 上述建边显然$n^2$爆炸。因为是区间建边,所以用线段树建边优化,不过这题比较特殊,只是点向区间连边,分析线段树建边原理,可以完全把出树省掉,就用一个入树连边就行了。(其实边数还是很多,所以边上界我开了$2 imes 10^7$。。。)
    • 这样缩点后DAG上找连通点数,有一道类似的题,不过最多数据只能出到$2000$,但是这题$n$是在$5e5$级别的,所以应当是和区间的特殊性质有一些关联的。`````发现每个炸弹引爆之后,向左向右引爆到的炸弹序号都必然是连续的一段。。所以只要对DAG上每个点可达的最大最小编号算一下就行了。。
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<queue>
     7 #define mst(x) memset(x,0,sizeof x)
     8 #define dbg(x) cerr << #x << " = " << x <<endl
     9 #define dbg2(x,y) cerr<< #x <<" = "<< x <<"  "<< #y <<" = "<< y <<endl
    10 using namespace std;
    11 typedef long long ll;
    12 typedef double db;
    13 typedef pair<int,int> pii;
    14 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    15 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    16 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
    17 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
    18 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
    19 template<typename T>inline T read(T&x){
    20     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    21     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    22 }
    23 const int N=5e5+7,P=1e9+7;
    24 int n,cnt,ans;
    25 struct thxorz{
    26     int head[N<<1],nxt[N*40],to[N*40],tot;
    27     inline void add(int x,int y){to[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
    28 }G1,G2;
    29 struct SGT{
    30     int id[N<<2];
    31     #define lc i<<1
    32     #define rc i<<1|1
    33     inline void build(int i,int L,int R){
    34         if(L==R){id[i]=L;return;}
    35         int mid=L+R>>1;id[i]=++cnt;
    36         build(lc,L,mid),build(rc,mid+1,R);
    37         G1.add(id[i],id[lc]),G1.add(id[i],id[rc]);
    38     }
    39     inline void update(int i,int L,int R,int ql,int qr,int x){
    40         if(ql<=L&&qr>=R){G1.add(x,id[i]);return;}
    41         int mid=L+R>>1;
    42         if(ql<=mid)update(lc,L,mid,ql,qr,x);
    43         if(qr>mid)update(rc,mid+1,R,ql,qr,x);
    44     }
    45 }T;
    46 ll pos[N],r[N];
    47 int dfn[N<<1],low[N<<1],stk[N<<1],instk[N<<1],bel[N<<1],minv[N<<1],maxv[N<<1],vis[N<<1],scc,top,tim;
    48 #define y G1.to[j]
    49 void tarjan(int x){
    50     dfn[x]=low[x]=++tim,stk[++top]=x,instk[x]=1;
    51     for(register int j=G1.head[x];j;j=G1.nxt[j]){
    52         if(!dfn[y])tarjan(y),MIN(low[x],low[y]);
    53         else if(instk[y])MIN(low[x],dfn[y]);
    54     }
    55     if(low[x]==dfn[x]){
    56         int tmp;++scc;
    57         do{
    58             instk[tmp=stk[top--]]=0,bel[tmp]=scc;
    59             if(tmp<=n)MIN(minv[scc],tmp),MAX(maxv[scc],tmp);
    60         }while(tmp^x);
    61     }
    62 }
    63 #undef y
    64 #define y G2.to[j]
    65 void dp(int x){
    66     if(vis[x])return;
    67     vis[x]=1;
    68     for(register int j=G2.head[x];j;j=G2.nxt[j])dp(y),MIN(minv[x],minv[y]),MAX(maxv[x],maxv[y]);
    69 }
    70 #undef y
    71 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
    72     cnt=read(n);memset(minv,0x3f,sizeof minv);
    73     for(register int i=1;i<=n;++i)read(pos[i]),read(r[i]);
    74     T.build(1,1,n);
    75     for(register int i=1;i<=n;++i){
    76         int lb=lower_bound(pos+1,pos+n+1,pos[i]-r[i])-pos;
    77         int rb=upper_bound(pos+1,pos+n+1,pos[i]+r[i])-pos-1;
    78         T.update(1,1,n,lb,rb,i);
    79     }
    80     for(register int i=1;i<=cnt;++i)if(!dfn[i])tarjan(i);
    81     #define y G1.to[j]
    82     for(register int i=1;i<=cnt;++i){
    83         for(register int j=G1.head[i];j;j=G1.nxt[j])
    84             if(bel[i]^bel[y])G2.add(bel[i],bel[y]);
    85     }
    86     #undef y
    87     for(register int i=1;i<=n;++i)dp(bel[i]),ans=(ans+i*1ll*(maxv[bel[i]]-minv[bel[i]]+1))%P;
    88     printf("%d
    ",ans);
    89     return 0;
    90 }
    View Code

    然后发现这种做法超级繁诶,对比一下榜rk1,时间,尤其是空间都很逊。。所以学习了一下原题正解。

    方法二:神仙递推

    首先对于每个炸弹,计算他最终能引爆的左边界和右边界。初始时,$lb_i=rb_i=i$。

    分析情况,会发现,有这几种引爆方式:

    • 一直沿着左边炸
    • 一直沿着右边炸
    • 先炸了左边,左边的触发了右边原本触发不了的(然后可能右边那个再触发更左边的,如此往复。。。)
    • 先炸了右边,然后。。。同上一个

    对于前两种,直接正一边反一遍推就行了。但是第三四种的话,需要从左至右对每个炸弹$i$先处理一下只考虑引爆左侧炸弹能拓展到的最远可达点$lb$,并且维护出一个这些可达引爆点中向右可达范围最远的距离表示$i$的新半径。然后,再从右向左推,对于每个点$i$,利用新半径去触发右侧点,并且用这些右侧点的$lb$和$rb$来更新自己。由于右边的边界处答案肯定是对的,向左的时候,只要把右边上一次的$lb$和$rb$合并过来,就可以求得这个点的$lb$和$rb$。

    是不是超有道理的。。

    具体还是看code。。不过我一开始因为不太理解思路,看了一个本来就写错了的老哥的题解,自己也写错了,成功被loj数据hack掉了,后来想了好长时间自己改了一下才过的。。大致是每次在拓展左边界时候更新半径,然后引爆右侧时候更新自己的答案。注意一下更新顺序,我就是这里被hack的。

    因为还不是特别理解,所以可能code还有问题,所以,欢迎hack。

     1 #include<bits/stdc++.h>
     2 #define rep(i,a,b) for(register int i(a);i<=b;++i)
     3 #define per(i,a,b) for(register int i(a);i>=b;--i)
     4 using namespace std;
     5 typedef long long ll;
     6 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
     7 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
     8 template<typename T>inline T read(T&x){
     9     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    10     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    11 }
    12 const int N=5e5+7,P=1e9+7;
    13 ll x[N],r[N];
    14 int lb[N],rb[N],n,ans;
    15 
    16 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
    17     read(n);rep(i,1,n)read(x[i]),read(r[i]),lb[i]=rb[i]=i;
    18     rep(i,1,n)while(lb[i]>1&&x[i]-x[lb[i]-1]<=r[i])MAX(r[i],r[lb[i]-1]-(x[i]-x[lb[i]-1])),lb[i]=lb[lb[i]-1];
    19     per(i,n,1)while(rb[i]<n&&x[rb[i]+1]-x[i]<=r[i])MIN(lb[i],lb[rb[i]+1]),rb[i]=rb[rb[i]+1];
    20     rep(i,1,n)ans=(ans+i*1ll*(rb[i]-lb[i]+1))%P;
    21     return printf("%d
    ",ans);
    22 }
    View Code

    忘说一件事。。上述做法是线性的,因为每次向左更新可达点,如果某次半径内有多个断开的块的时候,会把他们合并起来,这样所有块只会被合并一次。所以是线性的。

  • 相关阅读:
    HashMap 原理?jdk1.7 与 1.8区别
    内存泄漏与溢出
    NIO
    Mysql 存储引擎
    编写一个 rpc
    dubbo 与 zookeeper
    MongoDB
    natapp 内网穿透服务
    【AHOI 2009】同类分布
    [HNOI 2016] 序列
  • 原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/11771364.html
Copyright © 2020-2023  润新知