题面传送门

emmmm…………怎么评价这个题嘛。。。感觉纯论算法,此题根本谈不上难题,不过 WC 时候太智障只拿了个 48pts 就走人了。总之,技不如人,甘拜吓疯(

首先要注意到几件事情:

  1. 如果 \((x,y)\) 间存在合法的括号序列,那么 \((y,x)\) 之间也存在合法的括号序列,因为把一个路径反过来实际上相当于把括号序列翻转过来,并且左括号变右括号,右括号变左括号。
  2. 如果 \((x,y),(y,z)\) 之间存在合法的括号序列,那么 \((x,z)\) 之间也存在合法的括号序列。

我们将这两条综合在一起可得:存在合法括号序列的两点一定是若干个极大团的并。于是现在我们的任务就变为如何求这若干个团。

我们继续观察,又可得到一个性质:对于某个点 \(u\),如果存在某两个点 \(v,w\) 之间都存在连向 \(u\),左括号类型均为 \(i\) 的边,那么 \(v,w\) 之间肯定能互相到达,因为 \(v\to u\to w\) 就是形如 \(()\) 的合法路径。

如果我们将这个性质推广到一般团的情况,就有:对于同一个团中的两点 \(x,y\),如果存在某两个点 \(u,v\) 使得 \(u\) 与 \(x\) 之间、\(v\) 与 \(y\) 之间均有左括号类型为 \(i\) 的边,那么 \(u,v\) 就能互相到达,因为 \(x,y\) 本身就能互相到达,而在 \(x,y\) 的路径前面添一个 \(i\) 类型的左括号,后面添一个 \(i\) 类型的右括号,得到的仍是合法的括号序列。

这样一来我们就可以想出一个做法,先每个点单独成一个团,然后 \(n\) 开个 std::map 数组 \(mp_u\)。\(mp_{u,w}\) 表示以 \(u\) 为终点是否存在类型为 \(w\) 的边,如果有,那我们就记录第一次被访问的满足 \(v\) 与 \(u\) 之间存在类型为 \(w\) 的边的 \(v\)。然后每次新读入一条形如 \((u,v,w)\) 的边,我们就检查 \(mp_{v,w}\) 是否有值,如果有,那么说明 \(mp_{v,w},u\) 与 \(v\) 之间都存在类型为 \(w\) 的边,我们就将 \(mp_{v,w}\) 与 \(u\) 合并,否则我们就将 \(mp_{v,w}\) 设为 \(u\)。

然后考虑怎样合并两个集合。显然在合并以 \(u,v\) 为代表的两个集合的过程中,如果存在某个 \(w\) 使得 \(mp_{u,w},mp_{v,w}\) 都非零,那么意味着 \(mp_{u,w}\) 能够到达某个在 \(u\) 所代表的团中的点 \(x\),\(mp_{v,w}\) 能够到达某个在 \(v\) 所代表的团中的点 \(y\),而由于我们要将 \(u,v\) 所在的团合并成一个大团,所以 \(x,y\) 可以互相到达,这意味着 \(mp_{u,w},mp_{v,w}\) 也能互相到达,于是我们进一步合并 \(mp_{u,w},mp_{v,w}\)。否则如果某个值非零,不妨设 \(mp_{v,w}\) 非零,我们就令 \(mp_{u,w}=mp_{v,w}\),表示 \(mp_{v,w}\) 能够到达某个 \(u,v\) 合并形成的大团中的点。这样不断合并直到不能再合并中为止即可。

算下复杂度,对于每条边 \((u,v,w)\) 最多被合并一次,所以总共最多合并 \(m\) 次,而如果我们使用启发式合并,那么每个元素最多被合并 \(\log m\) 次。再加上 std::map 的复杂度,可知总复杂度为 \(m\log^2m\)。当然如果使用哈希表可将 std::map 的 \(\log\) 去掉,但懒得写了/cy

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=3e5;
int n,m,k,f[MAXN+5],siz[MAXN+5];
map<int,int> buc[MAXN+5];
queue<pii> q;
int find(int x){return (!f[x])?x:find(f[x]);}
void merge(int x,int y){
// printf("%d %d\n",x,y);
x=find(x);y=find(y);
if(x!=y){
if(siz[x]<siz[y]) swap(x,y);
ffe(it,buc[y]){
int col=it->fi,z=it->se;
if(buc[x][col]) q.push(mp(z,buc[x][col]));
else buc[x][col]=z;
} f[y]=x;siz[x]+=siz[y];
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) siz[i]=1;
for(int i=1;i<=m;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
if(buc[v][w]) q.push(mp(u,buc[v][w]));
else buc[v][w]=u;
}
while(!q.empty()){
pii p=q.front();q.pop();
merge(p.fi,p.se);
} ll ans=0;
for(int i=1;i<=n;i++) if(!f[i]) ans+=1ll*siz[i]*(siz[i]-1)/2;
printf("%lld\n",ans);
return 0;
}

洛谷 P7323 - [WC2021] 括号路径(启发式合并)的更多相关文章

  1. 洛谷 P3201 [HNOI2009]梦幻布丁(启发式合并)

    题面 luogu 题解 什么是启发式合并? 小的合并到大的上面 复杂度\(O(nlogn)\) 这题颜色的修改,即是两个序列的合并 考虑记录每个序列的\(size\) 小的合并到大的 存序列用链表 但 ...

  2. 【洛谷 P2764】 最小路径覆盖问题(最大流)

    题目链接 首先有\(n\)条路径,每条路径就是一个点,然后尽量合并,答案就是点数-合并数. 套路拆点,源连入,出连汇,原有的边入出连. 最大流就是最大合并数,第一问解决. 然后怎么输出方案? 我是找到 ...

  3. 【CSP-S 2019】【洛谷P5658】括号树【dfs】【二分】

    题目: 题目链接:https://www.luogu.org/problem/P5658?contestId=24103 本题中合法括号串的定义如下: () 是合法括号串. 如果 A 是合法括号串,则 ...

  4. 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths(tarjan求边双联通分量)

    题目描述 In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1. ...

  5. [洛谷P1730] 最小密度路径

    类型:Floyd 传送门:>Here< 题意:定义一条路径密度 = 该路径长度 / 边数.给出一张$DAG$,现有$Q$次询问,每次给出$X,Y$,问$X,Y$的最小密度路径($N \le ...

  6. 洛谷 P1739 表达式括号匹配

    题目链接https://www.luogu.org/problemnew/show/P1739 题目描述 假设一个表达式有英文字母(小写).运算符(+,—,*,/)和左右小(圆)括号构成,以“@”作为 ...

  7. 【洛谷】P1176: 路径计数2【递推】

    P1176 路径计数2 题目描述 一个N×N的网格,你一开始在(1,1),即左上角.每次只能移动到下方相邻的格子或者右方相邻的格子,问到达(N,N),即右下角有多少种方法. 但是这个问题太简单了,所以 ...

  8. 洛谷 P2860 [USACO06JAN]冗余路径Redundant Paths 解题报告

    P2860 [USACO06JAN]冗余路径Redundant Paths 题目描述 为了从F(1≤F≤5000)个草场中的一个走到另一个,贝茜和她的同伴们有时不得不路过一些她们讨厌的可怕的树.奶牛们 ...

  9. 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths

    题目描述 In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1. ...

随机推荐

  1. WSL (Windows Subsystem for Linux)

    WSL (Windows Subsystem for Linux) :适用于 Linux 的 Windows 子系统. References Install WSL with a single com ...

  2. k8s replicaset controller分析(1)-初始化与启动分析

    replicaset controller分析 replicaset controller简介 replicaset controller是kube-controller-manager组件中众多控制 ...

  3. 微信小程序的发布流程

    一.背景 在中大型的公司里,人员的分工非常仔细,一般会有不同岗位角色的员工同时参与同一个小程序项目.为此,小程序平台设计了不同的权限管理使得项目管理者可以更加高效管理整个团队的协同工作 以往我们在开发 ...

  4. the Agiles Scrum Meeting 3

    会议时间:2020.4.11 21:30 1.每个人的工作 今天已完成的工作 yjy:基本实现广播功能的前端 issues:小组任务1-增量开发组 wjx:基本实现注销功能的后端 issues:小组任 ...

  5. mongodb的索引操作

    在mongodb中,当我们一个集合中的数据量非常大时,比如几百万条数据,如果不使用索引,对数据的查询就会进行全表扫描,这个时候查询的速度就会非常的慢,此时我们就需要为集合建立上索引,从而加快查询的速度 ...

  6. dice_game攻防世界进阶区

    dice_game XCTF 4th-QCTF-2018 前言,不得不说,虽然是个简单题但是还是要记录一下,来让自己记住这些东西. 考察的知识点是: 1.cdll_loadlibrary加载对应库使得 ...

  7. GEOS使用记录

    由于需要计算GIS障碍物的缓冲区,所以研究了 一下GEOS库的使用,将使用的一些细节内容记录一下: 1.vs2010IDE无法编译较高版本的GEOS库,较高版本的库使用了更加高级的C++语法,如果想使 ...

  8. Python 语法错误 except Exception, e: ^ SyntaxError: invalid syntax

    出这个问题是因为python2和python3 语法有些不同 python2 和 3 处理 except 子句的语法有点不同,需要注意: Python2 try: print ("hello ...

  9. 2万字|30张图带你领略glibc内存管理精髓(因为OOM导致了上千万损失)

    前言 大家好,我是雨乐. 5年前,在上家公司的时候,因为进程OOM造成了上千万的损失,当时用了一个月的时间来分析glibc源码,最终将问题彻底解决. 最近在逛知乎的时候,发现不少人有对malloc/f ...

  10. “TCP:三次握手”分析——以一个简单的“服务器”和“客户端”为例

    linux&C这两天学到了网络编程这一章,自己写了一个小的"服务器"和"客户端"程序,目的在于简单理解tcp/ip模型,以及要搭建一台简单服务器,服务器 ...