zoukankan      html  css  js  c++  java
  • 【线段树 扫描线 二维数点】loj#6276. 果树

    路径计数转成二维数点很妙啊

    题目描述

    NiroBC 姐姐是个活泼的少女,她十分喜欢爬树,而她家门口正好有一棵果树,正好满足了她爬树的需求。

    这颗果树有 $N$ 个节点,标号 $1 ldots N$ 。每个节点长着一个果子,第 $i$ 个节点上的果子颜色为 $C_i$​ 。

    NiroBC 姐姐每天都要爬树,每天都要选择一条有趣的路径 $(u, v)$ 来爬。

    一条路径被称作有趣的,当且仅当这条路径上的果子的颜色互不相同。

    $(u, v)$ 和 $(v, u)$ 被视作同一条路径。特殊地, $(i, i)$ 也被视作一条路径,这条路径只含 $i$ 一个节点,显然是有趣的。

    NiroBC 姐姐想知道这颗树上有多少条有趣的路径。

    输入格式

    第一行,一个整数 $N$ ,表示果树的节点数目。

    接下来一行 $N$ 个整数 $C_{1 ldots N}$ ,表示 $N$ 个果子各自的颜色。

    再接下来 $N-1$ 行,每行两个整数 $u_i, v_i$​ ,表示 $u_i$​ 和 $v_i$​ 之间有一条边。

    数据保证这 $N-1$ 条边构成一棵树。

    输出格式

    一个整数,有趣的路径的数量。

    样例

    样例输入 1

    3
    1 2 3
    1 2
    1 3

    样例输出 1

    6

    样例输入 2

    5
    1 1 2 3 3
    1 2
    1 3
    2 4
    2 5

    样例输出 2

    8

    样例解释 1

    有 $(1,1)(1,2)(1,3)(2,2)(2,3)(3,3)$ 共 $6$ 条路径。

    样例解释 2

    有 $(1,1)(1,3)(2,2)(2,4)(2,5)(3,3)(4,4)(5,5)$ 共 $8$ 条路径。

    数据范围与提示

    对于所有数据, $1 le N le 10^5$ ,$1 le C_i le N$ ,每种颜色在树上出现不超过 $20$ 次。

    本题采用打包测试。

    各个 Subtask 的特殊限制如下,不填代表该项无特殊限制。

    Subtask 编号$N$其他限制该 Subtask 分值
    0 $le 100$   12
    1 $le 3000$   25
    2   整棵树形成一条依次为 $1, 2, 3, ldots , N$ 的链 30
    3     33

    题目分析

    二维数点

    注意到每种颜色的出现次数非常小,于是考虑枚举同种颜色。

    对于颜色相同的一对点,它们会把树分成三个部分,而两端部分不能够相互连边。这个部分用dfs序把图再构一遍,就方便处理了。至于两种分别是成链不成链的情况,稍微细心点细节就没问题。

    点对$(x,y)$可以表示为二维点$(x,y)$,也就是说每一次将非法位置转为二维矩形覆盖,那么最后就是枚举每一个点,对非法矩形差分扫描线处理。

    码力还是不够,标记${ m //HERE}$都是打挂的地方。

      1 #include<bits/stdc++.h>
      2 const int maxn = 100035;
      3 const int maxm = 200035;
      4 const int maxLog = 23;
      5 
      6 struct dfn
      7 {
      8     int l,r;
      9 }a[maxn];
     10 struct node
     11 {
     12     int mn,tot,tag;
     13 }f[maxn<<2];
     14 struct line
     15 {
     16     int l,r,opt;
     17     line (int a=0, int b=0, int c=0):l(a),r(b),opt(c) {}
     18 };
     19 long long ans;
     20 int n,c[maxn],fa[maxn][maxLog],dep[maxn],chTot;
     21 int edgeTot,head[maxn],nxt[maxm],edges[maxm];
     22 std::vector<int> col[maxn];
     23 std::vector<line> mdf[maxn];
     24 
     25 int read()
     26 {
     27     char ch = getchar();
     28     int num = 0, fl = 1;
     29     for (; !isdigit(ch); ch=getchar())
     30         if (ch=='-') fl = -1;
     31     for (; isdigit(ch); ch=getchar())
     32         num = (num<<1)+(num<<3)+ch-48;
     33     return num*fl;
     34 }
     35 void addedge(int u, int v)
     36 {
     37     edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
     38     edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot;
     39 }
     40 inline int kfa(int x, int d)
     41 {
     42     if (d==-1) return x;
     43     for (int i=20; i>=0; i--)
     44         if (d>>i&1) x = fa[x][i];
     45     return x;
     46 }
     47 void addLine(int l1, int r1, int l2, int r2)
     48 {
     49     if (l1 > r1||l2 > r2) return;
     50     mdf[l1].push_back(line(l2, r2, 1));
     51     mdf[r1+1].push_back(line(l2, r2, -1));
     52     mdf[l2].push_back(line(l1, r1, 1));
     53     mdf[r2+1].push_back(line(l1, r1, -1));
     54 }
     55 inline void split(int x, int y)
     56 {
     57     if (dep[x] > dep[y]) std::swap(x, y);
     58     int anc = kfa(y, dep[y]-dep[x]-1);        //HERE
     59     if (fa[anc][0]!=x||(dep[x]==dep[y]&&x!=y)) addLine(a[x].l, a[x].r, a[y].l, a[y].r);
     60     else{
     61         addLine(1, a[anc].l-1, a[y].l, a[y].r);
     62         addLine(a[anc].r+1, n, a[y].l, a[y].r);        //HERE    这里打挂只剩链30pts
     63     }
     64 }
     65 void smcolConnect()
     66 {
     67     register int i,j,k;
     68     for (i=1; i<=n; i++)
     69         for (j=0; j<col[i].size(); j++)
     70             for (k=j+1; k<col[i].size(); k++)
     71                 split(col[i][j], col[i][k]);        //HERE
     72 }
     73 void dfs(int x, int fat)
     74 {
     75     dep[x] = dep[fat]+1, fa[x][0] = fat;
     76     a[x].l = ++chTot;
     77     for (int i=head[x]; i!=-1; i=nxt[i])
     78         if (edges[i]!=fat) dfs(edges[i], x);
     79     a[x].r = chTot;
     80 }
     81 void build(int rt, int l, int r)
     82 {
     83     f[rt].tot = r-l+1;
     84     if (l==r) return;
     85     int mid = (l+r)>>1;
     86     build(rt<<1, l, mid);
     87     build(rt<<1|1, mid+1, r);
     88 }
     89 void init()
     90 {
     91     for (int j=1; j<=20; j++)
     92         for (int i=1; i<=n; i++)
     93             fa[i][j] = fa[fa[i][j-1]][j-1];
     94     build(1, 1, n);
     95 }
     96 void pushdown(int rt)
     97 {
     98     int l = rt<<1, r = rt<<1|1, &t = f[rt].tag;
     99     if (t){
    100         f[l].tag += t, f[r].tag += t;
    101         f[l].mn += t, f[r].mn += t, t = 0;            //HERE
    102     }
    103 }
    104 void pushup(int rt)
    105 {
    106     f[rt].mn = std::min(f[rt<<1].mn, f[rt<<1|1].mn);
    107     f[rt].tot = (f[rt].mn==f[rt<<1].mn?f[rt<<1].tot:0)+(f[rt].mn==f[rt<<1|1].mn?f[rt<<1|1].tot:0);
    108 }
    109 void modify(int rt, int L, int R, int l, int r, int c)
    110 {
    111     if (L <= l&&r <= R){
    112         f[rt].mn += c, f[rt].tag += c;
    113         return;
    114     }
    115     int mid = (l+r)>>1;
    116     pushdown(rt);      //HERE
    117     if (L <= mid) modify(rt<<1, L, R, l, mid, c);
    118     if (R > mid) modify(rt<<1|1, L, R, mid+1, r, c);
    119     pushup(rt);
    120 }
    121 int main()
    122 {
    123     memset(head, -1, sizeof head);
    124     n = read();
    125     for (int i=1; i<=n; i++) c[i] = read(), col[c[i]].push_back(i);
    126     for (int i=1; i<n; i++) addedge(read(), read());
    127     dfs(1, 0), init();
    128     smcolConnect();
    129     for (int i=1; i<=n; i++)
    130     {
    131         for (unsigned int p=0; p<mdf[i].size(); p++)
    132             modify(1, mdf[i][p].l, mdf[i][p].r, 1, n, mdf[i][p].opt);
    133         if (f[1].mn==0) ans += f[1].tot;
    134     }
    135     printf("%lld
    ",(ans+n)>>1);
    136     return 0;
    137 }

    DSU做法

    还看到一种神奇的dsu+可持久化线段树做法:LibreOJ #6276.果树 dsu on tree+可持久化线段树

    END

  • 相关阅读:
    C++ 类 构造函数 constructor
    数据库——关系代数
    海明码
    C++ this指针
    C++ 类的定义与实现
    C++ 函数 内联函数
    C++ 函数 函数的重载 有默认参数的函数
    2017年第八届蓝桥杯【C++省赛B组】
    2018年第九届蓝桥杯【C++省赛B组】
    C++ 函数 参数传递方式
  • 原文地址:https://www.cnblogs.com/antiquality/p/9925210.html
Copyright © 2011-2022 走看看