首页 > 其他分享 >P4434 [COCI2017-2018#2] ​​Usmjeri

P4434 [COCI2017-2018#2] ​​Usmjeri

时间:2023-03-09 22:33:25浏览次数:44  
标签:P4434 COCI2017 top 查集 int num 2018 lca operatorname

知识点:lca,种类并查集

新生赛原题。

什么嘛,我还是长了一点手的嘛

简述

给定一棵 \(n\) 个节点的树,初始时每条边方向不确定,同时给定 \(m\) 组约束,第 \(i\) 组约束为 \((a_i, b_i)\) 的形式,描述了一条树上路径 \(a_i\leftrightarrow b_i\)。要求给每条边定向,使得所有的路径 \(a_i \leftrightarrow b_i\) 中的边方向均相同,求可行的方案数,答案对 \(10^9 + 7\) 取模。
\(1\le n, m\le 3\times 10^5\)。
2S,256MB。

分析

有趣的做法,学到许多。

以 1 为根考虑,边的方向只有向上和向下两种,则每组约束实际上规定了边的朝向的种类关系。对于约束 \((a_i, b_i)\),记节点 \(a_i, b_i\) 的最近公共祖先为 \(\operatorname{lca}(a_i, b_i)\),则路径 \(a_i\rightarrow \operatorname{lca}(a_i, b_i)\) 上的所有边应属于同一种类、路径 \(b_i\rightarrow \operatorname{lca}(a_i, b_i)\) 上的所有边也属于同一种类;且当 \(\operatorname{lca}(a_i, b_i)\not= a_i \land \operatorname{lca}(a_i, b_i)\not= b_i\) 时,路径 \(a_i\rightarrow \operatorname{lca}(a_i, b_i)\) 上的边和 \(b_i\rightarrow \operatorname{lca}(a_i, b_i)\) 上的边属于不同种类。按照上述方案依次处理所有边的种类数时若出现矛盾则无解。

经过上述处理我们可以将所有边分为数个集合,同一集合内的边属于同种朝向或相反朝向,当确定其中一条边的朝向时,其余所有边的朝向均可确定。则对于同一种类内部的所有边,这些边的朝向有向上和向下两种取值。记这样的集合数量为 \(\operatorname{num}_1\),最终的方案数即为 \(2^{\operatorname{num}_1}\)。

考虑使用种类并查集维护这个过程。我们设每条边的编号为这条边连接的深度较深的节点的编号,则编号范围为 \(2\sim n\),它们对应并查集 \(2\sim n\)。另外建立 \(n-1\) 个虚并查集,代表每条边对应的种类相反的边,编号为 \(2+n\sim 2\times n\)。处理约束时,对于将路径 \(a_i\rightarrow \operatorname{lca}(a_i, b_i)\) 和路径 \(b_i\rightarrow \operatorname{lca}(a_i, b_i)\) 上相邻的边 \(x\) 和 \(y\),我们合并并查集 \(x, y\) 和并查集 \(x + n, y + n\);当 \(\operatorname{lca}(a_i, b_i)\not= a_i \land \operatorname{lca}(a_i, b_i)\not= b_i\) 时,我们合并并查集 \(a_i, b_i+n\) 和并查集 \(a_i + n, b_i\)。经过上述过程后统计这 \(2\times (n-1)\) 个并查集中合并后的的集合的数量 \(\operatorname{num}_2\) 。由于实并查集和虚并查集构成的集合是对称的,则在一开始的分析中所需的集合的数量 \(\operatorname{num}_1 = \operatorname{num}_2\),最终的方案数即为 \(2^{\frac{\operatorname{num}_2}{2}}\bmod 10^9 + 7\)。

进行链上相临的边合并时,暴力跳链复杂度过高,考虑对每条边都维护一个值 \(\operatorname{last}\),表示以这条边为最底端的链,向上合并到的深度最浅边的编号,可以另外使用一个并查集进行维护。跳链时依靠 \(\operatorname{last}\) 进行优化和路径压缩即可。

优化后跳链的总复杂度变为 \(O(n)\) 级别,则复杂度瓶颈是求 \(\operatorname{lca}\),代码总复杂度 \(O((n + m)\log n)\) 级别。

代码

//知识点:lca,种类并查集
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define LL long long
const int kN = 5e5 + 10;
const int kM = kN << 1;
const int p = 1e9 + 7;
//=============================================================
int n, m;
int edgenum, head[kN], v[kM], ne[kM];
int last[kN], f[kN << 2];
bool vis[kN << 1];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void Add(int u_, int v_) {
  v[++ edgenum] = v_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
int Find(int x_) {
  return f[x_] == x_ ? x_ : f[x_] = Find(f[x_]);
}
void Merge(int x_, int y_) {
  int fx = Find(x_), fy = Find(y_);
  f[fx] = fy;
}
namespace Cut {
  const int kNode = kN;
  int fa[kNode], son[kNode], dep[kNode], sz[kNode], top[kNode];
  void Dfs1(int u_, int fa_) {
    fa[u_] = fa_;
    sz[u_] = 1;
    dep[u_] = dep[fa_] + 1;
    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i];
      if (v_ == fa_) continue ;
      Dfs1(v_, u_);
      if (sz[v_] > sz[son[u_]]) son[u_] = v_;
      sz[u_] += sz[v_];
    }
  }
  void Dfs2(int u_, int top_) {
    top[u_] = top_;
    if (son[u_]) Dfs2(son[u_], top_);
    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i];
      if (v_ == fa[u_] or v_ == son[u_]) continue ;
      Dfs2(v_, v_);
    }
  }
  int Lca(int u_, int v_) {
    for (; top[u_] != top[v_]; u_ = fa[top[u_]]) {
      if (dep[top[u_]] < dep[top[v_]]) {
        std::swap(u_, v_);
      }
    }
    return dep[u_] < dep[v_] ? u_ : v_;
  }
  int Findlast(int x_) {
    return last[x_] == x_ ? x_ : last[x_] = Findlast(last[x_]);
  }
  void MergeLast(int x_, int y_) {
    int lastx = Findlast(x_), lasty = Findlast(y_);
    last[lastx] = lasty;
  }
  void Dfs3(int u_, int end_) {
    u_ = Findlast(u_);
    int fau_ = Findlast(fa[u_]);
    if (dep[fa[u_]] <= dep[end_]) return ;
    Merge(u_, fau_), Merge(u_ + n, fau_ + n);
    MergeLast(u_, fau_);
    Dfs3(fau_, end_);
  }
}
int Qpow(int x_, int y_) {
  int ret = 1;
  while (y_) {
    if (y_ & 1) ret = 1ll * ret * x_ % p;
    y_ >>= 1, x_ = 1ll * x_ * x_ % p;
  }
  return ret;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  n = read(), m = read();
  for (int i = 1; i < n; ++ i) {
    int u_ = read(), v_ = read();
    Add(u_, v_), Add(v_, u_);
  }
  Cut::Dfs1(1, 0), Cut::Dfs2(1, 1);
  
  for (int i = 1; i <= 2 * n; ++ i) f[i] = i;
  for (int i = 2; i <= n; ++ i) last[i] = i;
  while (m --) {
    int u_ = read(), v_ = read(), lca = Cut::Lca(u_, v_);
    if (lca == u_ || lca == v_) {
      if (lca == u_) std::swap(u_, v_);
      Cut::Dfs3(u_, v_);
    } else {
      Merge(u_, v_ + n), Merge(u_ + n, v_);
      Cut::Dfs3(u_, lca);
      Cut::Dfs3(v_, lca);
    }
  }

  int ans = 0;
  for (int i = 2; i <= 2 * n; ++ i) {
    if (i == n + 1) continue;
    int fai = Find(i);
    if (i <= n && Find(i) == Find(i + n)) {
      printf("0\n");
      return 0;
    }
    ans += !vis[fai];
    vis[fai] = 1;
  }
  printf("%d\n", Qpow(2, ans / 2));
  return 0;
}

标签:P4434,COCI2017,top,查集,int,num,2018,lca,operatorname
From: https://www.cnblogs.com/luckyblock/p/17201740.html

相关文章

  • BUU pwn PicoCTF_2018_shellcode //最简单的shellcode
    这道题需要我们了解x86汇编的lea指令。leadst,src指的是dst=&srcfile可知32bitELFIDAF5发现反编译失败,查看main函数的汇编,发现会调用vuln函数。其中leaeax,[ebp+va......
  • 【NOI2018】冒泡排序
    【NOI2018】冒泡排序Description最近,小S对冒泡排序产生了浓厚的兴趣。为了问题简单,小S只研究对\(1\)到\(n\)的排列的冒泡排序。下面是对冒泡排序的算法描述。......
  • FPGA 学习笔记:Vivado 2018.2 MicroBlaze Uartlite 配置
    前言Vivado版本:Vivado2018.2+VivadoHLS2018.2,VivadoHLS2018.2用于SDK开发,C语言开发创建基于MicroBlaze的【BlockDesign】后,添加了【AXIUartlite】,发现烧写......
  • csp201803-2
    主要思路就是:如果存在位置相同的两球,那么其前进方向*-1;或者球在端点,*-1,按时间累加即可。#include<bits/stdc++.h>usingnamespacestd;inta[105];intflag[105];int......
  • csp201809-4
    这是一道差分约束求最长路的图的问题:通过已知的条件可以容易列出以下不等式:2*a1<=x1+x2<=2*a1+13*a2<=x1+x2+x3<=3*a2+23*a3<=x2+x3+x4<=3*a3+2       ......
  • petalinux2018.3编译sdk失败的解决办法
    由于公司用的xilinx产品,大都是老版本,因此在转linux时,为减少切换麻烦,petalinux也是用的2018.3编译kernel/u-boot/root-fs一切正常,但在编译SDK时,报失败。失败信息如下:NOTE......
  • 2018GPLT (天梯赛训练03-01)
    2018GPLT (天梯赛训练03-01)1.天梯赛座位分配这题在网上都没找到篇靠谱的解答很多都是错的(可能但凡会点编程的人都不会被这题卡住了吧)(悲)从我的好朋友毛的程序我觉得......
  • 一文搞懂weblogic CVE-2018-2628原理与利用
    参考:http://xxlegend.com/2018/06/20/CVE-2018-2628简单复现和分析/在CVE-2017-3248的利用中,我们用ysoserial生成了一个java.rmi.registry.Registry类型的proxy首先回......
  • 【题解】CTT 2018 Day 2
    目录A.宝石游戏B.面国建设C.WechatA.宝石游戏无修改时考虑长链剖分(加整体异或标记)。有修改时分块,不考虑关键点层长链剖分,最后算答案的时候处理关键点层的答案即可。......
  • 题解 北大集训2018 点分治
    题意给定\(n\)个点的树,求点分治方案数,对\(10^9+7\)取模。两种方案不同当且仅当点分树不同。\(1\leqn\leq5000\)题解考虑怎样合并两个点分治方案。如果我们有......