[IOI2018]werewolf狼人

好像很久没更博客了呢~

[IOI2018]werewolf狼人

题目链接

洛谷

题解

可以将题意转化为
求从\(S\)出发只经过大于\(L\)的点构成的集合\(∩\)\(E\)只经过小于\(R\)的点是否为空。
我们建\(2\)\(kruskal\)重构树\(A,B\)
\(A\)为每条边的两个点中取较小的点为权值所构成的最大生成树对应的重构树。
\(B\)为每条边的两个点中取较大的点为权值所构成的最小生成树对应的重构树。
接着这\(2\)个集合直接在\(A,B\)上倍增找一下就好了。
同时我们把每个点的开始\(dfs\)序和结束\(dfs\)序求出来。
这就变成了一个序列问题。
直接找个数据结构维护一下就好了。

代码

#include <bits/stdc++.h>

namespace con_fast_io {
char buf[1 << 12], *p1 = buf, *p2 = buf, SR[1 << 23], z[23]; int Z = 0, C = -1;
inline void Ot() { fwrite(SR, 1, C + 1, stdout); C = -1; }
inline void flush() { if (C > 1 << 22) Ot(); }
inline char getc() {
  return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 12, stdin), p1 == p2) ? EOF : *p1++;
}
template<class t> inline void read(t& x) {
  x = 0; int fl = 1; char nc;
  while (nc = getc(), (nc < 48 || nc > 57) && (~nc)) if (nc == 45) fl = -1;
  x = nc - 48; while (nc = getc(), 47 < nc && nc < 58) x = (x << 1) + (x << 3) + (nc ^ 48);
  x *= fl;
};
template<class t> inline void write(t x, char c) {
  int y = 0; if (x < 0) y = 1, x = -x; while (z[++Z] = x % 10 + 48, x /= 10);
  if (y) z[++Z] = '-'; while(SR[++C] = z[Z], Z--); SR[++C] = c; flush();
}
inline void write(char* s) {
  int le = strlen(s);
  for (int i = 0; i < le; i++) SR[++C] = *s++;
  flush();
}
/*char z[23]; int Z = 0;
inline void Ot() {}
template<class t> inline void read(t& x) {
  x = 0; int fl = 1; char nc;
  while (nc = getchar(), (nc < 48 || nc > 57) && (~nc)) if (nc == 45) fl = -1;
  x = nc - 48; while (nc = getchar(), 47 < nc && nc < 58) x = (x << 1) + (x << 3) + (nc ^ 48);
  x *= fl;
}
template<class t> inline void write(t x, char c) {
  Z = 0; int y = 0; if (x < 0) y = 1, x = -x; z[++Z] = c;
  while (z[++Z] = x % 10 + 48, x /= 10);
  if (y) z[++Z] = '-'; while (Z) putchar(z[Z--]);
}
inline void write(char* s) { printf("%s\n", s); }*/
}
using namespace con_fast_io;
const int maxn = 2e5 + 600;
const int maxm = 4e5 + 1500;
const int inf = 0x3f3f3f3f;

int n, m, i, j, k, q, cnto, val[maxn];
struct edge {
  int u, v; edge() { u = v = 0; } edge(int _u, int _v) { u = _u, v = _v; }
} e[maxm];
inline bool cmp1(edge a, edge b) { return std::min(a.u, a.v) > std::min(b.u, b.v); }
inline bool cmp2(edge a, edge b) { return std::max(a.u, a.v) < std::max(b.u, b.v); }
struct ufs_t {
  int fa[maxm];
  inline void init(int j) { for (int i = 1; i <= j; i++) fa[i] = i; }
  int find(int u) { return u == fa[u] ? u : fa[u] = find(fa[u]); }
} ufs;
struct kruskal_rebuild_tree_t {
  int cnte, tim, hd[maxm], ver[maxm], nxt[maxm], fa[maxm][25], st[maxm], ed[maxm];
  int val[maxm], dfn[maxm];
  inline void adde(int u, int v) {
    ver[++cnte] = v, nxt[cnte] = hd[u], hd[u] = cnte;
  }
  void dfs(int u) {
    if (u <= n) dfn[++tim] = u, val[u] = u, st[u] = tim; else st[u] = tim + 1;
    for (int i = 1; i <= 20; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (int i = hd[u], v; i; i = nxt[i]) {
      v = ver[i];
      fa[v][0] = u;
      dfs(v);
    }
    ed[u] = tim;
  }
  inline void work(int type) {
    ufs.init(n << 1);
    if (!type) std::sort(e + 1, e + m + 1, cmp1);
    else std::sort(e + 1, e + m + 1, cmp2);
    int tot = n;
    for (int i = 1; i <= m; i++) {
      int u = e[i].u, v = e[i].v, w = type ? std::max(u, v) : std::min(u, v);
      int ru = ufs.find(u), rv = ufs.find(v);
      if (ru == rv) continue;
      ufs.fa[ru] = ufs.fa[rv] = ++tot;
      val[tot] = w, adde(tot, ru), adde(tot, rv);
    }
   // exit(0);
    dfs(tot);
  }
  inline int getL(int u, int l) {
    for (int i = 20; ~i; i--) if (val[fa[u][i]] >= l) u = fa[u][i];
    return u;
  }
  inline int getR(int u, int r) {
    for (int i = 20; ~i; i--) if (val[fa[u][i]] <= r) u = fa[u][i];
    return u;
  }
  inline void out() {
    for (int i = 1; i <= n; i++) {
      printf("%d %d %d\n", st[i], ed[i], val[i]);
    }
  }
} A, B;
int rt[maxn * 35], ch[maxn * 35][2], sz[maxn * 35];
inline void cpy(int o1, int o2) {
  ch[o1][0] = ch[o2][0], ch[o1][1] = ch[o2][1], sz[o1] = sz[o2];
}
void modify(int p, int& u, int l, int r) {
  cpy(++cnto, u); sz[u = cnto]++;
  if (l == r) return;
  int mid = (l + r) >> 1;
  if (p <= mid) modify(p, ch[u][0], l, mid);
  else modify(p, ch[u][1], mid + 1, r);
}
int query(int ql, int qr, int l, int r, int o1, int o2) {
  if (!(o1 || o2) || ql > r || qr < l) return 0;
  if (ql <= l && r <= qr) return sz[o2] - sz[o1];
  int mid = (l + r) >> 1, res = 0;
  return query(ql, qr, l, mid, ch[o1][0], ch[o2][0]) + query(ql, qr, mid + 1, r, ch[o1][1], ch[o2][1]);
}

int main() {
  read(n), read(m), read(q);
  for (int i = 1, u, v; i <= m; i++) {
    read(u), read(v), u++, v++;
    e[i] = edge(u, v);
  }
  A.work(0), B.work(1), B.val[0] = inf;
//  A.out(), B.out();
  for (int i = 1; i <= n; i++) val[A.dfn[i]] = i;
 // return 0;
  for (int i = 1; i <= n; i++) modify(val[B.dfn[i]], rt[i] = rt[i - 1], 1, n);
 // return 0;
  for (int i = 1, S, E, l, r; i <= q; i++) {
    read(S), read(E), read(l), read(r); S++, E++, l++, r++;
    int a = A.getL(S, l), b = B.getR(E, r);
    write(query(A.st[a], A.ed[a], 1, n, rt[B.st[b] - 1], rt[B.ed[b]]) ? 1 : 0, '\n');
  }
  return Ot(), 0;
}

转载于:https://www.cnblogs.com/Sai0511/p/11489235.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值