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

题目描述

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)$,也就是说每一次将非法位置转为二维矩形覆盖,那么最后就是枚举每一个点,对非法矩形差分扫描线处理。

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

 #include<bits/stdc++.h>
const int maxn = ;
const int maxm = ;
const int maxLog = ; struct dfn
{
int l,r;
}a[maxn];
struct node
{
int mn,tot,tag;
}f[maxn<<];
struct line
{
int l,r,opt;
line (int a=, int b=, int c=):l(a),r(b),opt(c) {}
};
long long ans;
int n,c[maxn],fa[maxn][maxLog],dep[maxn],chTot;
int edgeTot,head[maxn],nxt[maxm],edges[maxm];
std::vector<int> col[maxn];
std::vector<line> mdf[maxn]; int read()
{
char ch = getchar();
int num = , fl = ;
for (; !isdigit(ch); ch=getchar())
if (ch=='-') fl = -;
for (; isdigit(ch); ch=getchar())
num = (num<<)+(num<<)+ch-;
return num*fl;
}
void addedge(int u, int v)
{
edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot;
}
inline int kfa(int x, int d)
{
if (d==-) return x;
for (int i=; i>=; i--)
if (d>>i&) x = fa[x][i];
return x;
}
void addLine(int l1, int r1, int l2, int r2)
{
if (l1 > r1||l2 > r2) return;
mdf[l1].push_back(line(l2, r2, ));
mdf[r1+].push_back(line(l2, r2, -));
mdf[l2].push_back(line(l1, r1, ));
mdf[r2+].push_back(line(l1, r1, -));
}
inline void split(int x, int y)
{
if (dep[x] > dep[y]) std::swap(x, y);
int anc = kfa(y, dep[y]-dep[x]-); //HERE
if (fa[anc][]!=x||(dep[x]==dep[y]&&x!=y)) addLine(a[x].l, a[x].r, a[y].l, a[y].r);
else{
addLine(, a[anc].l-, a[y].l, a[y].r);
addLine(a[anc].r+, n, a[y].l, a[y].r); //HERE    这里打挂只剩链30pts
}
}
void smcolConnect()
{
register int i,j,k;
for (i=; i<=n; i++)
for (j=; j<col[i].size(); j++)
for (k=j+; k<col[i].size(); k++)
split(col[i][j], col[i][k]); //HERE
}
void dfs(int x, int fat)
{
dep[x] = dep[fat]+, fa[x][] = fat;
a[x].l = ++chTot;
for (int i=head[x]; i!=-; i=nxt[i])
if (edges[i]!=fat) dfs(edges[i], x);
a[x].r = chTot;
}
void build(int rt, int l, int r)
{
f[rt].tot = r-l+;
if (l==r) return;
int mid = (l+r)>>;
build(rt<<, l, mid);
build(rt<<|, mid+, r);
}
void init()
{
for (int j=; j<=; j++)
for (int i=; i<=n; i++)
fa[i][j] = fa[fa[i][j-]][j-];
build(, , n);
}
void pushdown(int rt)
{
int l = rt<<, r = rt<<|, &t = f[rt].tag;
if (t){
f[l].tag += t, f[r].tag += t;
f[l].mn += t, f[r].mn += t, t = ; //HERE
}
}
void pushup(int rt)
{
f[rt].mn = std::min(f[rt<<].mn, f[rt<<|].mn);
f[rt].tot = (f[rt].mn==f[rt<<].mn?f[rt<<].tot:)+(f[rt].mn==f[rt<<|].mn?f[rt<<|].tot:);
}
void modify(int rt, int L, int R, int l, int r, int c)
{
if (L <= l&&r <= R){
f[rt].mn += c, f[rt].tag += c;
return;
}
int mid = (l+r)>>;
pushdown(rt);      //HERE
if (L <= mid) modify(rt<<, L, R, l, mid, c);
if (R > mid) modify(rt<<|, L, R, mid+, r, c);
pushup(rt);
}
int main()
{
memset(head, -, sizeof head);
n = read();
for (int i=; i<=n; i++) c[i] = read(), col[c[i]].push_back(i);
for (int i=; i<n; i++) addedge(read(), read());
dfs(, ), init();
smcolConnect();
for (int i=; i<=n; i++)
{
for (unsigned int p=; p<mdf[i].size(); p++)
modify(, mdf[i][p].l, mdf[i][p].r, , n, mdf[i][p].opt);
if (f[].mn==) ans += f[].tot;
}
printf("%lld\n",(ans+n)>>);
return ;
}

DSU做法

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

END

【线段树 扫描线 二维数点】loj#6276. 果树的更多相关文章

  1. hdu 2795 线段树(二维问题一维化)

    Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  2. 6.6 省选模拟赛 线段 二维数点问题 树套树 CDQ分治

    LINK:线段 还是太菜了 没看出这道题真正的模型 我真是一个典型的没脑子选手. 考虑如何查询答案. 每次在一个线段x的状态被更改后 可以发现有影响的是 和x相连那段极长连续1子段. 设这个子段左端点 ...

  3. loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分

    $ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinob ...

  4. 洛谷 P4088 [USACO18FEB] Slingshot P(线段树+二维数点)

    题目链接 题意:有一个数轴,上面有 \(n\) 个传送门,使用第 \(i\) 个传送门,你可以从 \(x_i\) 走到 \(y_i\),花费的时间为 \(t_i\) 秒.你的速度为 \(1\) 格/秒 ...

  5. 【BZOJ4785】[Zjoi2017]树状数组 树套树(二维线段树)

    [BZOJ4785][Zjoi2017]树状数组 Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一 ...

  6. BZOJ4822[Cqoi2017]老C的任务——树状数组(二维数点)

    题目描述 老 C 是个程序员.     最近老 C 从老板那里接到了一个任务——给城市中的手机基站写个管理系统.作为经验丰富的程序员,老 C 轻松 地完成了系统的大部分功能,并把其中一个功能交给你来实 ...

  7. BZOJ1935: [Shoi2007]Tree 园丁的烦恼(树状数组 二维数点)

    题意 题目链接 Sol 二维数点板子题 首先把询问拆成四个矩形 然后离散化+树状数组统计就可以了 // luogu-judger-enable-o2 #include<bits/stdc++.h ...

  8. 【bzoj5173】[Jsoi2014]矩形并 扫描线+二维树状数组区间修改区间查询

    题目描述 JYY有N个平面坐标系中的矩形.每一个矩形的底边都平行于X轴,侧边平行于Y轴.第i个矩形的左下角坐标为(Xi,Yi),底边长为Ai,侧边长为Bi.现在JYY打算从这N个矩形中,随机选出两个不 ...

  9. 线段树扫描线(一、Atlantis HDU - 1542(覆盖面积) 二、覆盖的面积 HDU - 1255(重叠两次的面积))

    扫描线求周长: hdu1828 Picture(线段树+扫描线+矩形周长) 参考链接:https://blog.csdn.net/konghhhhh/java/article/details/7823 ...

随机推荐

  1. python爬虫——web前端基础(4)

    CSS,指层叠样式表,用来定义如何显示HTML元素,一般和HTML配合使用. 在HTML中使用CSS样式的方法: 内联样式表:CSS代码直接写在现有的HTML标记中,直接使用style属性改变样式.例 ...

  2. css Masks

    css Masks:添加蒙板: 测试在微信端可以支持了.谷歌浏览器支持.safari应该也是支持的. 效果:http://runjs.cn/code/xrrgmgmk 但是谷歌可以支持这样子的:htt ...

  3. 2.排序检索数据 ---SQL

    order by 一.排序数据 SELECT prod_name FROM Products ORDER BY prod_name; ORDER BY子句的位置 在指定一条ORDER BY子句时,应该 ...

  4. 题解 P1004 方格取数

    传送门 动态规划Yes? 设i为路径长度,(为什么i这一维可以省掉见下)f[j][k]表示第一个点到了(j,i-j),第二个点到了(k,j-k) 则 int ji=i-j,ki=i-k; f[j][k ...

  5. hdu4403- A very hard Aoshu problem(搜索)

    枚举等号的位置,然后暴力搜索一波 这个题本身不难,但它是我第一次使用对拍程序来查找错误,值得纪念. #include<cstdio> #include<string.h> #i ...

  6. JS代码运行延迟

    还是上篇文章的项目. 现在是屏幕上需要显示九张图表,刚好用一张3X3的表格来显示.但是负责这块内容的同事始终没法让九张图表同时显示,有些图表的位置空了出来. 大家百思不得其解,最后只得求助技术经理. ...

  7. Jmeter4.0----响应断言(6)

    1.说明 一个HTTP请求发出去,怎么判断执行的任务是否成功呢?通过检查服务器响应数据,是否返回预期想要的数据,如果是,判断任务成功,反之任务失败. 作用:判断请求是否成功 2.步骤 第一步:添加 “ ...

  8. 什么是.NET for Apache Spark?

    什么是.NET for Apache Spark? 分享一个.NET平台开源免费跨平台的大数据分析框架.NET for Apache Spark for Apache Spark   今天早上六点半左 ...

  9. P4869 罪犯分组

    思路: 明显的dp,虽然我想到了二进制模拟,想到了转移,但还是先看了题解,原来真是这样,,,,不是第三题吗? 用f[i]表示,对于前i个罪犯最少需要分几组. 对于每个状态用二进制表示,第i位上1,0表 ...

  10. java类及编写public类的基础点

    1.一个java文件中只能有一个public类.且公共类名称必须与java文件名一致,否则会出现错误提示.与其他面向对象编程语言的一样,在利用java分析问题时,基本思路即为将问题的属性(静)与行为( ...