Tsinsen-A1491 家族【并查集】

问题描述

  阿狸和桃子养了n个小阿狸, 小阿狸们每天都在一起玩的很开心. 作为工程师的阿狸在对小阿狸们之间的关系进行研究以后发现了小阿狸的人际关系由某种神奇的相互作用决定, 阿狸称之为“键”. 每个键有一个频率, 称为键频率, 是一个整数(单位Hz).
  由于小阿狸们每天成集团地黏在一起, 桃子希望他们能够分成更加独立的几团. 阿狸发现, 一旦小阿狸们分开, 独立的一块连在一起的几个小阿狸就会形成一个家族, 而家族的类型由这个家族的小阿狸的数量唯一确定(比如说只有一个小阿狸的家族显然就是单身码农, 两个小阿狸的显然是一对小阿狸恋人, 三个小阿狸的就是三口之家等等). 显然, 一个小阿狸和另一个小阿狸处于同一家族, 当且仅当两个小阿狸之间存在直接或间接的键组成的路径.
  桃子对每种小阿狸家族都有自己的喜好程度, 她希望所有的小阿狸家族喜好程度之和大于等于K.
  为了让小阿狸们分开来, 阿狸决定让某些键断裂, 只保留某一段频率的键, 比如说100Hz到140Hz频率的键, 这时频段宽度为40Hz. 当然, 阿狸希望频段宽度越小越好, 但至少要有一个小键. 你的任务就是求出最小的频段宽度.
  注意, 输入不保证全部键都有效时只有一个小阿狸家族.

输入格式

  第一行3个整数n(<=1000), m(<=5000), K(0~2^31-1).
  接下来1行n个整数, 第k的整数表示桃子对大小为k的小阿狸家族的喜爱程度.
  接下来m行, 每行3个整数, u, v, f. 表示u小阿狸和小阿狸v之间存键, 频率f Hz.

输出格式

  一个整数, 即最窄的频段宽度(不存在可行频段, 输出"T_T", 不含引号).

样例输入

4 4 52
1 50 2 9
1 2 6
2 3 8
3 4 4
1 4 3

样例输出

0

样例说明

  频段3Hz~3Hz或4Hz~4Hz或6Hz~6Hz或8Hz~8Hz

样例输入

4 4 10
1 5 2 9
1 2 6
2 3 8
3 4 4
1 4 3

样例输出

2

样例说明

  频段4Hz~6Hz

样例输入

4 4 10
1 4 2 9
1 2 6
2 3 8
3 4 4
1 4 3

样例输出

T_T

数据规模和约定

  对于 30% 的数据, n <=10
  对于 50% 的数据, n <=50 , m <=200
  对于 100% 的数据, n <=1000 , m <=3000

题解

  首先m<=5000,看不到这个就世界再见。

  一开始是肯定是把边排序,然后来枚举,我一开始是这样写的,两个两个,三个三个,四个四个枚举,然后就退化成n^3方了。

  Ngshily大爷讲了题解,我们在确定起点的情况下,依次把终点向后移动,每次考虑新加入的边造成的家族的影响,然后就是n^2了。

 1 #include <bits/stdc++.h>
 2 #define rep(i, a, b) for (int i = a; i <= b; i++)
 3 #define drep(i, a, b) for (int i = a; i >= b; i--)
 4 #define REP(i, a, b) for (int i = a; i < b; i++)
 5 #define mp make_pair
 6 #define pb push_back
 7 #define clr(x) memset(x, 0, sizeof(x))
 8 #define xx first
 9 #define yy second
10 using namespace std;
11 typedef long long i64;
12 typedef pair<int, int> pii;
13 const int inf = ~0U >> 1;
14 const i64 INF = ~0ULL >> 1;
15 //********************************
16 const int maxn = 1005, maxm = 5005;
17 pair<int, pii> edge[maxm];
18 int val[maxn], father[maxn], sze[maxn];
19 inline int getfather(int x) { return father[x] == x ? x : father[x] = getfather(father[x]); }
20 int main() {
21     int n, m, k;
22     scanf("%d%d%d", &n, &m, &k);
23     rep(i, 1, n) scanf("%d", &val[i]);
24     rep(i, 1, m) scanf("%d%d%d", &edge[i].yy.xx, &edge[i].yy.yy, &edge[i].xx);
25     sort(edge + 1, edge + 1 + m);
26     int ret = inf;
27     rep(i, 1, m) {
28         rep(o, 1, n) sze[o] = 1, father[o] = o;
29         int ans = n * val[1];
30         rep(j, i, m) {
31             if (j != i && edge[j].xx != edge[j - 1].xx) {
32                 if (ans >= k) {
33                     ret = min(ret, edge[j - 1].xx - edge[i].xx);
34                     break;
35                 }
36             }
37             int fx = getfather(edge[j].yy.xx), fy = getfather(edge[j].yy.yy);
38             if (fx != fy) {
39                 father[fy] = fx;
40                 ans -= val[sze[fy]] + val[sze[fx]];
41                 sze[fx] += sze[fy];
42                 sze[fy] = 0;
43                 ans += val[sze[fx]];
44             }
45         }
46         if (ans >= k) ret = min(ret, edge[m].xx - edge[i].xx);
47     }
48     if (ret == inf) puts("T_T");
49     else printf("%d\n", ret);
50     return 0;
51 }

时间: 2024-10-31 18:48:53

Tsinsen-A1491 家族【并查集】的相关文章

codevs 1073 家族 并查集

没什么可以说的,并查集裸题,标签难度和实际不成正比啊,存个板子: 1 #include<stdio.h> 2 #include<string.h> 3 #define maxn 23333 4 int n,m,p,fa[maxn]; 5 int getf(int); 6 int find(int,int); 7 int main() 8 { 9 scanf("%d %d %d",&n,&m,&p); 10 for(int i=1;i<

[tsA1491][2013中国国家集训队第二次作业]家族[并查集]

m方枚举,并查集O(1)维护,傻逼题,,被自己吓死搞成神题了... 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 struct tri { int x,y,z; bool operator<(const tri & temp)const { return z<temp.z; } }; 6 7 int n,m,k,Sum,Ans=0x7fffffff,a[1100],Size[1100],f[1100]; 8

BZOJ 3454 家族 并查集

题目大意:给定一张无向图,每个点有边权,给每个联通块大小一个喜爱度,求一个最小的区间,使保留这个区间内的所有边权的边时喜爱度之和最大 n<=1000,m<=5000 脑残没法治系列-- 如果暴力枚举区间并每次计算喜爱度,时间复杂度为O(nm^2),超时 固定一个左端点,将右端点右移,每次用并查集加边并维护喜爱度之和,时间复杂度O(m^2) 然后这题就做完了= = #include <cstdio> #include <cstring> #include <iost

【codevs1073/P1551】家族/亲戚——并查集

题目链接:codevs,洛谷 这道题就是并查集的基础题,getf函数寻找该节点的祖先,要注意在查找的时候可以顺便把路上的节点的父节点也改为当前祖先(即路径压缩). 询问的时候不能因为两个点的父节点不同就说他们不是同一棵树上的,要一直找到他们的祖先再做比较. 具体实现细节看代码: #include<cstdio> #include<cstring> using namespace std; int f[5005]; int read() { int ans=0,f=1;char c=

CODEVS1073 家族 (并查集)

一道裸的并查集,练练手不错. program CODEVS1073; var i,j,m,n,q,x,y,k1,k2,z:longint; f:array[0..10000] of longint; function find(x:longint):longint; var fa:longint; begin while f[x]=x do exit(x); fa:=find(f[x]); f[x]:=fa; exit(fa); end; begin readln(n,m,q); for i:=

并查集 之 1073 家族

/* 并查集 */ 1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstddef> 5 #include <iterator> 6 #include <algorithm> 7 #include <string> 8 #include <locale> 9 #include <cmath> 10

并查集 (Union-Find Sets)及其应用

定义 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.常常在使用中以森林来表示. 集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并. 主要操作 初始化 把每个点所在集合初始化为其自身. 通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N). 查找 查找元素所在的集合,即根节点. 合并 将两个元素所在的集合合并为一个集合. 通常来说,合并之前,应先判断两个元素是否属于

并查集小记

并查集: 并查集,一种树型的数据结构,处理一些不相交集合的合并及查询问题.比如问题:某个家族人员过于庞大,要判断两个人是否是亲戚,不太容易.现给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系. 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚.如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚. 从基本实现集合的结构出发: 1.单纯的快速查找: 若id相同则在一个集合中,下图中,( 2, 3, 4, 9 )为一集合, 3 和 6 不在一个集合中 合并集合时,需逐个比较将

hdu 1213 并查集入门

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1213 How Many Tables Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 12538    Accepted Submission(s): 6145 Problem Description Today is Ignatius' b

并查集——家谱

题目描述 现代的人对于本家族血统越来越感兴趣,现在给出充足的父子关系,请你编写程序找到某个人的最早的祖先. 输入格式: 输入文件由多行组成,首先是一系列有关父子关系的描述,其中每一组父子关系由二行组成,用#name 的形式描写一组父子关系中的父亲的名字,用+name 的形式描写一组父子关系中的儿子的名字:接下来用?name 的形式表示要求该人的最早的祖先:最后用单独的一个$表示文件结束.规定每个人的名字都有且只有 6 个字符,而且首字母大写,且没有任意两个人的名字相同.最多可能有 1000 组父