zoukankan      html  css  js  c++  java
  • [IOI2018]狼人

    嘟嘟嘟


    好题好题。


    看到对边权或点权的限制,可以想kruskal重构树(好吧我是为了刷kruskal重构树才写这道题的……)。


    给题目变个型:(Q)组询问,每组询问为:一匹狼从终点出发,一个人从起点出发,狼只能经过编号为([1, R])的点,人只能经过([L, n])的点,问能否有一个公共点。


    那么我们可以给狼和人分别建一棵kruskal重构树,对于人,我们把边权按(min(E[i].u, E[i].v))从大到小排序;对于狼,把边权按(max(E[i].u, E[i].v))从小到大排序。
    以人为例,重构树上节点(u)的子树就表示经过点权不超过(u)的点权能互相到达的点。那么我们从起点(S)向上倍增,找到最靠上的一个点(u),满足他的点权仍不小于(L)。这样这个点子树内的点就是人能活动的范围。
    狼也同理,找到最考上的点(v)
    这样我们只用判断(u)(v)子树范围内的点有没有交集即可。这是一个二维数点问题。所谓的二位数点,就是横坐标是狼的dfs序,纵坐标是人的dfs序,然后看这个矩形内是否有点。可以在线主席树,也可以离线树状数组。


    我写的在线主席树。方法就是我们先按狼的重构树的dfs序建树,每一个位置的权值是这个点在人的重构树上的dfs序。这样对于一个询问,我们只用看第(dfn[v])(dfn[v] + size[v] - 1)棵主席树中,区间([dfn[u], dfn[u] + siz[u] - 1])中有没有数,有就返回1,否则返回0.


    这题在loj上可以当传统题写,但必须一下子全读完在一个个回答询问,边读边回答询问会交互超时……

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<assert.h>
    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 = 4e6 + 5;
    const int N = 19;
    In 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;
    }
    In void write(ll x)
    {
      if(x < 0) x = -x, putchar('-');
      if(x >= 10) write(x / 10);
      putchar(x % 10 + '0');
    }
    In void MYFILE()
    {
    #ifndef mrclr
      freopen("ac.in", "r", stdin);
      freopen("ha.out", "w", stdout);
    #endif
    }
    
    int n, m, Q;
    struct edges {int x, y;} E[maxn];
    struct Edge {int nxt, to;};
    
    In bool cmp1(edges a, edges b) {return min(a.x, a.y) > min(b.x, b.y);}
    In bool cmp2(edges a, edges b) {return max(a.x, a.y) < max(b.x, b.y);}
    
    struct Kruskal
    {
      Edge e[maxn];
      int head[maxn], ecnt = -1, a[maxn];
      In void addEdge(int x, int y)
      {
        e[++ecnt] = (Edge){head[x], y};
        head[x] = ecnt;
      }
    
      In int Find(int x) {return x == p[x] ? x : p[x] = Find(p[x]);}
      int tcnt, p[maxn];
      In void build(bool flg)
      {
        Mem(head, -1); tcnt = n;
        for(int i = 1; i <= n; ++i) p[i] = i, a[i] = 0;
        for(int i = 1; i <= m; ++i)
          {
    	int px = Find(E[i].x), py = Find(E[i].y);
    	if(px == py) continue;
    	a[++tcnt] = flg ? min(E[i].x, E[i].y) : max(E[i].x, E[i].y), p[tcnt] = tcnt;
    	addEdge(tcnt, px), addEdge(tcnt, py);
    	p[px] = tcnt, p[py] = tcnt;
          }
      }
    
      int dep[maxn], siz[maxn], fa[N + 2][maxn];
      int dfn[maxn], pos[maxn], cnt;
      In void dfs(int now)
      {
        siz[now] = 1;
        dfn[now] = ++cnt, pos[cnt] = now;
        for(int i = 1; (1 << i) <= dep[now]; ++i)
          fa[i][now] = fa[i - 1][fa[i - 1][now]];
        for(int i = head[now], v; ~i; i = e[i].nxt)
          {
    	v = e[i].to;
    	dep[v] = dep[now] + 1, fa[0][v] = now;
    	dfs(v);
    	siz[now] += siz[v];
          }
      }
      In int jump(int x, int K, int flg)
      {
        for(int i = N; i >= 0; --i)
          if((1 << i) <= dep[x] && a[fa[i][x]] * flg >= K * flg) x = fa[i][x];
        return x;
      }
    }K1, K2;
    
    struct Tree
    {
      int ls, rs, sum;
    }t[maxn * 20];
    int root[maxn], tcnt = 0;
    In void insert(int old, int& now, int l, int r, int id)
    {
      t[now = ++tcnt] = t[old];
      ++t[now].sum;
      if(l == r) return;
      int mid = (l + r) >> 1;
      if(id <= mid) insert(t[old].ls, t[now].ls, l, mid, id);
      else insert(t[old].rs, t[now].rs, mid + 1, r, id);
    }
    int FLG = 0;
    In void query(int old, int now, int l, int r, int L, int R)
    {
      if(!now) return;
      if(FLG) return;
      if(l == L && r == R) {FLG |= t[now].sum - t[old].sum; return;}
      int mid = (l + r) >> 1;
      if(R <= mid) query(t[old].ls, t[now].ls, l, mid, L, R);
      else if(L > mid) query(t[old].rs, t[now].rs, mid + 1, r, L, R);
      else query(t[old].ls, t[now].ls, l, mid, L, mid), query(t[old].rs, t[now].rs, mid + 1, r, mid + 1, R);
    }
    
    int _S[maxn], _T[maxn], _L[maxn], _R[maxn];
    
    int main()
    {
      //MYFILE();
      n = read(), m = read(), Q = read();
      for(int i = 1; i <= m; ++i)
        {
          E[i].x = read() + 1, E[i].y = read() + 1;
        }
      for(int i = 1; i <= Q; ++i) _S[i] = read() + 1, _T[i] = read() + 1, _L[i] = read() + 1, _R[i] = read() + 1;
      sort(E + 1, E + m + 1, cmp1), K1.build(1);
      sort(E + 1, E + m + 1, cmp2), K2.build(0);
      K1.dep[K1.tcnt] = 0, K1.cnt = 0, K1.dfs(K1.tcnt);
      K2.dep[K2.tcnt] = 0, K2.cnt = 0, K2.dfs(K2.tcnt);
      for(int i = 1; i <= K2.cnt; ++i)
        if(K2.pos[i] <= n) insert(root[i - 1], root[i], 1, K1.cnt, K1.dfn[K2.pos[i]]);
        else root[i] = root[i - 1];
      for(int i = 1; i <= Q; ++i)
        {
          int x = K1.jump(_S[i], _L[i], 1), y = K2.jump(_T[i], _R[i], -1);
          FLG = 0;
          query(root[K2.dfn[y] - 1], root[K2.dfn[y] + K2.siz[y] - 1], 1, K1.cnt, K1.dfn[x], K1.dfn[x] + K1.siz[x] - 1);
          write(FLG ? 1 : 0), enter;
        }
      return 0;
    }
    
  • 相关阅读:
    BT协议分析(1)—1.0协议
    Qt线程(2) QThread中使用WorkObject
    新浪微博的开放平台官方文档太粗略,记:仿大平台来实现
    58同城 骗子太多
    代码实现业务经验(程序员的核心能力)
    gitbash 本地文件提交为一个新的项目 到 gitlab
    Spring 核心容器 IOC
    spring AOP 理解
    java不返回某些字段,包括 null
    CentOS7安装 Redis5 单实例
  • 原文地址:https://www.cnblogs.com/mrclr/p/10985239.html
Copyright © 2011-2022 走看看