zoukankan      html  css  js  c++  java
  • [SNOI2017]炸弹

    嘟嘟嘟


    这题有一些别的瞎搞神奇做法,而且复杂度似乎更优,不过我为了练线段树,就乖乖的官方正解了。


    做法就是线段树优化建图+强连通分量缩点+DAGdp。
    如果一个炸弹(i)能引爆另一个炸弹(j),就从(i)(j)连边。然后我们从图上每一个点dfs,能走到的点就是他最终能引爆的炸弹数量。
    但这个复杂度显然不行。首先连边太多。然后显而易见的是一个炸弹能引爆的炸弹范围是一个连续区间,所以用线段树优化一下就好了。
    然后每一个点dfs显然也太捞。所以我们先缩点,然后在DAG上反向dp就很棒棒了。
    缩点和dp的时候维护每一个点能向左和向右到达最远的区间,这样每一个点能引爆的炸弹数量就是区间长度了。
    刚开始我想直接维护炸弹数量。但第一个困难是有一些点是线段树上的虚点(非叶子节点),不应该被算上,然后我就想给这个点附上0的权值,到时候加权值就好了。第二个困难是dp的时候两个点的引爆范围可能重叠,那么单纯的数量相加就必定会gg了。


    写起来不难。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<stack>
    #include<queue>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 5e5 + 5;
    const int maxN = 1e6 + 5;
    const int maxe = 2e7 + 5;
    const ll mod = 1e9 + 7;
    inline ll read()
    {
      ll ans = 0;
      char ch = getchar(), last = ' ';
      while(!isdigit(ch)) last = ch, ch = getchar();
      while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
      if(last == '-') ans = -ans;
      return ans;
    }
    inline void write(ll x)
    {
      if(x < 0) x = -x, putchar('-');
      if(x >= 10) write(x / 10);
      putchar(x % 10 + '0');
    }
    
    int n, du[maxN];
    ll pos[maxn], rad[maxn];
    struct Edge
    {
      int nxt, to;
    }e[maxe], e2[maxe];
    int head[maxN], ecnt = -1, head2[maxN], ecnt2 = -1;
    In void addEdge(int x, int y)
    {
      e[++ecnt] = (Edge){head[x], y};
      head[x] = ecnt;
    }
    In void addEdge2(int x, int y)
    {
      ++du[y];
      e2[++ecnt2] = (Edge){head2[x], y};
      head2[x] = ecnt2;
    }
    
    In ll inc(ll a, ll b) {return a + b < mod ? a + b : a + b - mod;}
    
    int tIn[maxn << 2], l[maxn << 2], r[maxn << 2], id[maxN], tcnt = 0;
    In void build(int L, int R, int now)
    {
      l[now] = L; r[now] = R;
      if(L == R) {tIn[now] = L; id[L] = now; return;}
      tIn[now] = ++tcnt; id[tcnt] = now;
      int mid = (L + R) >> 1;
      build(L, mid, now << 1);
      build(mid + 1, R, now << 1 | 1);
      addEdge(tIn[now], tIn[now << 1]);
      addEdge(tIn[now], tIn[now << 1 | 1]);
    }
    In void update(int L, int R, int now, int x)
    {
      if(l[now] == L && r[now] == R)
        {
          addEdge(x, tIn[now]);
          return;
        }
      int mid = (l[now] + r[now]) >> 1;
      if(R <= mid) update(L, R, now << 1, x);
      else if(L > mid) update(L, R, now << 1 | 1, x);
      else update(L, mid, now << 1, x), update(mid + 1, R, now << 1 | 1, x);
    }
    
    bool in[maxN];
    int st[maxN], top = 0;
    int dfn[maxN], low[maxN], cnt = 0;
    int col[maxN], Min[maxN], Max[maxN], ccol = 0;
    In void dfs(int now)
    {
      dfn[now] = low[now] = ++cnt;
      st[++top] = now; in[now] = 1;
      for(int i = head[now], v; ~i; i = e[i].nxt)
        {
          if(!dfn[v = e[i].to])
    	{
    	  dfs(v);
    	  low[now] = min(low[now], low[v]);
    	}
          else if(in[v]) low[now] = min(low[now], dfn[v]);
        }
      if(dfn[now] == low[now])
        {
          int x; ++ccol;
          do
    	{
    	  x = st[top--]; in[x] = 0;
    	  col[x] = ccol;
    	  Min[ccol] = min(Min[ccol], l[id[x]]); 
    	  Max[ccol] = max(Max[ccol], r[id[x]]);
    	  //得另开一个数组反向记图中的点在线段树上的标号
    	}while(x ^ now);
        }
    }
    
    In void buildGraph(int now)
    {
      int u = col[now];
      for(int i = head[now], v; ~i; i = e[i].nxt)
        {
          if(u == (v = col[e[i].to])) continue;
          addEdge2(v, u);  //建反图
        }
    }
    
    In void topo()
    {
      queue<int> q;
      for(int i = 1; i <= tcnt; ++i) if(!du[i]) q.push(i);
      while(!q.empty())
        {
          int now = q.front(); q.pop();
          for(int i = head2[now], v; ~i; i = e2[i].nxt)
    	{
    	  v = e2[i].to;
    	  Min[v] = min(Min[v], Min[now]);
    	  Max[v] = max(Max[v], Max[now]);
    	  if(!--du[v]) q.push(v);
    	}
        }
    }
    
    int main()
    {
      Mem(head, -1); Mem(head2, -1);
      n = read();
      tcnt = n, build(1, n, 1);
      for(int i = 1; i <= n; ++i) pos[i] = read(), rad[i] = read();
      for(int i = 1; i <= n; ++i)
        {
          int L = lower_bound(pos + 1, pos + i + 1, pos[i] - rad[i]) - pos;
          int R = upper_bound(pos + i + 1, pos + n + 1, pos[i] + rad[i]) - pos - 1;
          update(L, R, 1, i);
        }
      fill(Min + 1, Min + tcnt + 1, INF);
      for(int i = 1; i <= tcnt; ++i) if(!dfn[i]) dfs(i);
      for(int i = 1; i <= tcnt; ++i) buildGraph(i);
      topo();
      ll ans = 0;
      for(int i = 1; i <= n; ++i) ans = inc(ans, 1LL * i * (Max[col[i]] - Min[col[i]] + 1) % mod);
      write(ans), enter;
      return 0;
    }
    
  • 相关阅读:
    好用的PasswordTextBox.
    可以修改Autocomplete高度和宽度的TextBox.(ComboBox也试用)
    Show WER and DMP file information
    在webBrowser中触发html页面中的javaScript.
    Trigger in sql server
    黑客来了。。。键盘钩子,听起来很高端。
    Send email
    (VB.net)自定义TableLayoutPanel使它能够在运行时用鼠标改变行高和列宽。
    (C#) Format the cell of DataGridView based on the TextBox.Text
    可以用来测显示屏的inch数。
  • 原文地址:https://www.cnblogs.com/mrclr/p/10781716.html
Copyright © 2011-2022 走看看