洛谷 P7323 - [WC2021] 括号路径(启发式合并)
emmmm…………怎么评价这个题嘛。。。感觉纯论算法,此题根本谈不上难题,不过 WC 时候太智障只拿了个 48pts 就走人了。总之,技不如人,甘拜吓疯(
首先要注意到几件事情:
- 如果 \((x,y)\) 间存在合法的括号序列,那么 \((y,x)\) 之间也存在合法的括号序列,因为把一个路径反过来实际上相当于把括号序列翻转过来,并且左括号变右括号,右括号变左括号。
- 如果 \((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] 括号路径(启发式合并)的更多相关文章
- 洛谷 P3201 [HNOI2009]梦幻布丁(启发式合并)
题面 luogu 题解 什么是启发式合并? 小的合并到大的上面 复杂度\(O(nlogn)\) 这题颜色的修改,即是两个序列的合并 考虑记录每个序列的\(size\) 小的合并到大的 存序列用链表 但 ...
- 【洛谷 P2764】 最小路径覆盖问题(最大流)
题目链接 首先有\(n\)条路径,每条路径就是一个点,然后尽量合并,答案就是点数-合并数. 套路拆点,源连入,出连汇,原有的边入出连. 最大流就是最大合并数,第一问解决. 然后怎么输出方案? 我是找到 ...
- 【CSP-S 2019】【洛谷P5658】括号树【dfs】【二分】
题目: 题目链接:https://www.luogu.org/problem/P5658?contestId=24103 本题中合法括号串的定义如下: () 是合法括号串. 如果 A 是合法括号串,则 ...
- 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths(tarjan求边双联通分量)
题目描述 In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1. ...
- [洛谷P1730] 最小密度路径
类型:Floyd 传送门:>Here< 题意:定义一条路径密度 = 该路径长度 / 边数.给出一张$DAG$,现有$Q$次询问,每次给出$X,Y$,问$X,Y$的最小密度路径($N \le ...
- 洛谷 P1739 表达式括号匹配
题目链接https://www.luogu.org/problemnew/show/P1739 题目描述 假设一个表达式有英文字母(小写).运算符(+,—,*,/)和左右小(圆)括号构成,以“@”作为 ...
- 【洛谷】P1176: 路径计数2【递推】
P1176 路径计数2 题目描述 一个N×N的网格,你一开始在(1,1),即左上角.每次只能移动到下方相邻的格子或者右方相邻的格子,问到达(N,N),即右下角有多少种方法. 但是这个问题太简单了,所以 ...
- 洛谷 P2860 [USACO06JAN]冗余路径Redundant Paths 解题报告
P2860 [USACO06JAN]冗余路径Redundant Paths 题目描述 为了从F(1≤F≤5000)个草场中的一个走到另一个,贝茜和她的同伴们有时不得不路过一些她们讨厌的可怕的树.奶牛们 ...
- 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths
题目描述 In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1. ...
随机推荐
- Java继承、重写与重载
1.java继承 1.1概念 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为. 继承可以使用extends和implem ...
- 如何配置log4Net
之前曾经用过几次,但是每次都是用完就忘了,下次再用的时候要baidu半天,这次弄通之后直接记下来. 步骤如下. 1. 安装log4Net,直接用NuGet, Install-Package log4N ...
- sip信令跟踪工具sngrep
概述 在VOIP的使用过程中,最常见的问题就是信令不通和语音质量问题. 通常的问题跟踪手段包括日志分析.抓包分析. 抓包的工具有wireshark.tcpdump等等,如果是只针对sip信令的抓包,则 ...
- 模拟赛18 T1 施工 题解
前言: 真的是不容易啊.这个题在考场上想到了最关键的性质,但是没写出来. 后来写出来,一直调,小错不断. 没想到改的最后一个错误是两个int 乘起来爆了int 其实最后我还是觉得复杂度很假.\(n^2 ...
- 《基于SIR的路边违停行为传播模型研究》
My Focus: 路边违停 行为的传播模型; 学习基于SIR XXX模型的可行性分析.建立和结论分析 Author: 左忠义,王英英,包蕴 Mind Map:
- Python中的括号()、[]、{}
长时间不用容易混淆,仅记! 在Python语言中最常见的括号有三种,分别是:小括号().中括号[].花括号{} . Python中的小括号(): 代表tuple元祖数据类型,元祖是一种不可变序列.大多 ...
- 洛谷 P5785 [SDOI2012] 任务安排
链接: P5785 弱化版:P2365 题意: 有 \(n\) 个任务待完成,每个任务有一个完成时间 \(t_i\) 和费用系数 \(f_i\),相邻的任务可以被分成一批.从零时刻开始这些任务会被机器 ...
- [个人开源]vue-code-view:一个在线编辑、实时预览的代码交互组件
组件简介 vue-code-view是一个基于 vue 2.x.轻量级的代码交互组件,在网页中实时编辑运行代码.预览效果的代码交互组件. 使用此组件, 不论 vue 页面还是 Markdown 文档中 ...
- 结束的NULL
最近同学叫我帮忙看个问题,为啥这个循环没有退出, 代码如下,原本是想拿到最后的NULL指针就可以结束循环 #include <stdio.h> #include <stdlib.h& ...
- hdu 3038 How Many Answers Are Wrong(并查集)
题意: N和M.有N个数. M个回答:ai, bi, si.代表:sum(ai...bi)=si.如果这个回答和之前的冲突,则这个回答是假的. 问:M个回答中有几个是错误的. 思路: 如果知道sum( ...