• bzoj 5017 [Snoi2017]炸弹


    题面

    https://www.lydsy.com/JudgeOnline/problem.php?id=5017

    题解

    如果数据范围小一点那么就缩点 然后跑一个基础的DAG上的dp就好了

    但是边数是$O(n^2)$的 所以就会炸

    然后发现题目的特殊性

    每一个点连向的点是连续的 换言之就是每个点和一个连续的段连上了一条边

    那么我们把段拆开 用类似线段树的做法拆成log个小段 然后把这个点与这些小段连边即可

    记得把每个段向它的儿子连上边

    这样点数和边数都是$O(n log n)$ 就可以了

    Code

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 typedef long long ll;
      4 
      5 ll read(){
      6     ll x=0,f=1;char c=getchar();
      7     while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
      8     while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
      9     return x*f;
     10 }
     11 
     12 const int mod=1e9+7;
     13 const int maxn=500500;
     14 int n,nwn;
     15 ll pos[maxn],rad[maxn];
     16 struct Node{
     17     int l,r;
     18 } tr[maxn*4];
     19 
     20 int dy[maxn];
     21 
     22 inline bool pd(int a,int b){
     23     return abs(pos[a]-pos[b])<=rad[a];
     24 }
     25 
     26 int head[maxn*5],nxt[maxn*20],to[maxn*20],cnt;
     27 int rhead[maxn*5],rnxt[maxn*20],rto[maxn*20],rcnt;
     28 
     29 inline void add_edge(int a,int b){
     30     to[++cnt]=b;
     31     nxt[cnt]=head[a];
     32     head[a]=cnt;
     33     rto[++rcnt]=a;
     34     rnxt[rcnt]=rhead[b];
     35     rhead[b]=rcnt;
     36 }
     37 
     38 void build(int i,int l,int r){
     39     tr[i].l=l,tr[i].r=r;
     40     nwn=max(nwn,i);
     41     if(l==r){
     42         dy[l]=i;return;
     43     }
     44     int md=(l+r)>>1;
     45     build(i*2,l,md);
     46     build(i*2+1,md+1,r);
     47     add_edge(i,i*2);
     48     add_edge(i,i*2+1);
     49 }
     50 
     51 inline void work(int ind,int i,int l,int r){
     52     if(tr[i].l>r || tr[i].r<l) return;
     53     if(tr[i].l>=l && tr[i].r<=r){
     54         add_edge(dy[ind],i);
     55         return;
     56     }
     57     work(ind,i*2,l,r);
     58     work(ind,i*2+1,l,r);
     59 }
     60 
     61 int vis[maxn*4];
     62 int seq[maxn*4],tot;
     63 int col;
     64 
     65 void dfs1(int nw){
     66     vis[nw]=1;
     67     for(int i=rhead[nw];i;i=rnxt[i]){
     68         int v=rto[i];
     69         if(vis[v]) continue;
     70         dfs1(v);
     71     }
     72     seq[++tot]=nw;
     73 }
     74 
     75 void dfs2(int nw){
     76     vis[nw]=col;
     77     for(int i=head[nw];i;i=nxt[i]){
     78         int v=to[i];
     79         if(vis[v]) continue;
     80         dfs2(v);
     81     }
     82 }
     83 
     84 int sz[maxn*4];
     85 int ans[maxn*4];
     86 bool vis2[maxn*4];
     87 
     88 void dfs3(int nw){
     89     if(ans[vis[nw]]==0) ans[vis[nw]]=sz[vis[nw]];
     90     vis2[nw]=1;
     91     for(int i=head[nw];i;i=nxt[i]){
     92         int v=to[i];
     93         if(vis2[v]) continue;
     94         dfs3(v);
     95         if(vis[v]!=vis[nw])
     96             ans[vis[nw]]+=ans[vis[v]];
     97     }
     98 }
     99 
    100 int main(){
    101     #ifdef LZT
    102     freopen("in","r",stdin);
    103     #endif
    104     n=read();
    105     build(1,1,n);
    106     for(int i=1;i<=n;i++)
    107         pos[i]=read(),rad[i]=read();
    108     int l,r;
    109     for(int i=1;i<=n;i++){
    110         int L=1,R=i;
    111         while(L<=R){
    112             int md=(L+R)>>1;
    113             if(pos[i]-pos[md]>rad[i]) L=md+1;
    114             else R=md-1,l=md;
    115         }
    116         L=i,R=n;
    117         while(L<=R){
    118             int md=(L+R)>>1;
    119             if(pos[md]-pos[i]>rad[i]) R=md-1;
    120             else L=md+1,r=md;
    121         }
    122         work(i,1,l,r);
    123     }
    124     
    125     for(int i=1;i<=nwn;i++){
    126         if(tr[i].l==0) continue;
    127         if(!vis[i]) dfs1(i);
    128     }
    129     memset(vis,0,sizeof(vis));
    130     
    131     for(int i=tot;i>=1;i--)
    132         if(!vis[seq[i]]) ++col,dfs2(seq[i]);
    133     
    134     for(int i=1;i<=nwn;i++) if(tr[i].l && tr[i].l==tr[i].r) sz[vis[i]]++;
    135     dfs3(1);
    136     
    137     ll res=0;
    138     for(int i=1;i<=nwn;i++){
    139         if(tr[i].l<tr[i].r) continue;
    140         res+=tr[i].l*1ll*ans[vis[i]]%mod;
    141         res%=mod;
    142     }
    143     printf("%lld
    ",res);
    144     
    145     return 0;
    146 }
    147 
    148 /*
    149 4
    150 1 1
    151 5 1
    152 6 5
    153 15 15
    154 */
    View Code

    Review

    动机?

    首先缩点+dp是显然的

    然后我就没有想到怎么优化

    其实也不难想啊 就是要把连续的一段点转化成几个点 也就是区间->几个点 那么就上线段树嘛

    然后写的时候注意总点数的处理

    按照我的线段树写法 一棵树中 最小编号到最大编号之间不连续 也就是有一些编号不对应任何点

    这样在遍历所有点的时候要判断一下当前编号是否存在

  • 相关阅读:
    程序写法
    2011年C++再次给力
    WIN7+LINUX双系统
    随机洗牌算法
    Eclipse快捷键大全
    Android 编程规范
    android Context 上下文的几点解析
    消息模式Toast.makeText的几种常见用法
    Eclipse的优化
    用PULL解析器解析XML文件
  • 原文地址:https://www.cnblogs.com/wawawa8/p/9368746.html
Copyright © 2020-2023  润新知