[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;
}
原文地址:https://www.cnblogs.com/mrclr/p/10985239.html