洛谷 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版人脸检测详解上篇:运行环境的Docker镜像(CentOS+JDK+OpenCV)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 【二食堂】Alpha - Scrum Meeting 4
Scrum Meeting 4 例会时间:4.14 12:30 - 12:50 进度情况 组员 昨日进度 今日任务 李健 1. 主页面的搭建工作issue 1. 完成主页搭建**issue2. 与后端 ...
- linux与windows下文件编码问题
注:转换操作均在Linux终端进行操作 DOS与Unix格式转换 安装工具:dos2unix.unix2dos # ubuntu apt-get install dos2unix apt-get in ...
- TVS管性能及选型总结
https://wenku.baidu.com/view/5b5bda5526fff705cc170af8.html
- STM32直流电机启动(一)驱动电路的介绍
驱动电路 典型的H桥驱动电路如下:要使电机旋转只需导通对角线上的两个三极管即可,如导通Q1,Q4,关闭Q2,Q4即可驱动电机正转:若想电机反向转动,即导通三极管Q2,Q3,关闭Q1,Q4.此时电路图可 ...
- Swift-技巧(三)使用元组(tuple)
最近看 iOS 的官方功能的 Demo 时,发现代码中使用元组的地方很多,所以兴趣上来,查了下元组的出处. 在苹果的文档中就只有简短的两句,使用元组创建一个组合的值,从函数中返回多个值.元组中的可以使 ...
- 为什么IDEA不推荐你使用@Autowired ?
@Autowired注解相信每个Spring开发者都不陌生了!在DD的Spring Boot基础教程和Spring Cloud基础教程中也都经常会出现. 但是当我们使用IDEA写代码的时候,经常会发现 ...
- 数据代理Object.defineProperty()
数据代理: 通过一个对象代理对另一个对象中属性的操作(读/写) 数据代理 Object.defineProperty() Object.defineProperty() 方法会直接在一个对象上定义一个 ...
- 前端调试工具DevTools处理网络请求
DevTools处理网络请求 位置:network 1.是否启用网络处理功能 2.清除历史 3.过滤器,自定义筛选 4.是否保留先前的历史,因为每次刷新会删除历史重新加载,选中后新老请求都在可做对比 ...
- PTA 列车调度 (25分)
PTA 列车调度 (25分) [程序实现] #include<bits/stdc++.h> using namespace std; int main(){ int num,n; cin& ...