题意:
就是给你一个数组,但是这些数组有一定的约束条件然后给你这些约束条件,然后你判断在这些约束条件下数组是否成立
首先,这个题目一给,想到的就是并查集!
并查集
并查集就是来判断两个元素是不是在同一个集合里面,他其实就是一个森林,然后有一个数组f存放他的父节点,然后当 f i = i f_i=i fi=i的时候,他的父节点就是他自己,即他就是根节点
并查集有两个基本的操作:find
和merge
int find(int x)
{
if(f[x]==x) return x;
else return find(f[x]);
}
void merge(int a,int b)//将两个树的根节点合并
{
f[find(a)]=find(b);
}
路径压缩
但这个样子直接查找,复杂度也很高,所以这个时候要用到路径压缩
我们只需要利用一个结点的最早的祖先,那么只需要每次find
操作过程中,把这个结点的爸爸改成它最早的祖先。
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
离散化
“离散化”是把无穷大集合中的若干个元素映射为有限集合以便于统计的方法
首先由题目ii,jj的规模:1<=i,j<=1e9 可知需要离散化
离散化,有两种方法
离散化后重复元素相同
1、用一个数组将离散的数据存下来。
2、排序,后面要二分。
3、去重,因为要保证相同的元素离散化后数字相同。
4、索引,再用二分把离散化后的数字放回原数组。
n 原数组大小
num 原数组中的元素
lsh 离散化的数组
cnt 离散化后的数组大小
#include<algorithm> // 头文件
const int MAXN = 1e6+4;
int lsh[MAXN], cnt, num[MAXN], n;
for (int i=1; i<=n; i++) {
scanf("%d",&num[i]);
lsh[i] = num[i];
}
sort(lsh+1 , lsh+n+1);//排序
cnt = unique(lsh+1, lsh+n+1) - lsh - 1;//去重
//二分查找
for(int i=1; i<=n; i++) {
num[i] = lower_bound(lsh+1 , lsh+cnt+1 , num[i]) - lsh;
}
其中两个函数
unique()
unique的作用是“去掉”容器中相邻元素的重复元素(不一定要求数组有序),它会把重复的元素添加到容器末尾(所以数组大小并没有改变),而返回值是去重之后的尾地址
lower_bound()
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
在从小到大的排序数组中,lower_bound(begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
而在从大到小的数组中就要用到函数重载了
lower_bound( begin,end,num,greater() ): 从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdio>
using namespace std;
const int N=1000007;
int num[N],lsh[N*3];
struct node//记录所读入的数
{
int x,y,e;
} a[N];
bool cmp(node a,node b)
{
return a.e>b.e;
}
int _find(int x)
{
if(x==num[x])return x;
return num[x]=_find(num[x]);
}
inline void init(int x)//初始化
{
for(int i=1; i<=x; i++)
num[i]=i;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(num,0,sizeof num);
memset(a,0,sizeof a);
memset(lsh,0,sizeof lsh);
int flag=1;
int tans=-1;
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].e);
lsh[++tans]=a[i].x;
lsh[++tans]=a[i].y;
}
sort(lsh,lsh+tans);//离散化开始了
int k=unique(lsh,lsh+tans)-lsh;//去重
for(int i=1; i<=n; i++)
{
a[i].x=lower_bound(lsh,lsh+k,a[i].x)-lsh;
a[i].y=lower_bound(lsh,lsh+k,a[i].y)-lsh;
}
init(k);
sort(a+1,a+1+n,cmp);//按e来排序,先把可以相等的合并在一起,再判断不相等的是否能够满足
for(int i=1; i<=n; i++)
{
int f1=_find(a[i].x);
int f2=_find(a[i].y);
if(a[i].e)
num[f1]=f2;
else if(f1==f2)
{
printf("NO\n");
flag=0;
break;
}
}
if(flag)printf("YES\n");
}
return 0;
}
这一题过的实在太艰难了,可能还是对知识点不太熟的原因吧!
关于1LL
(一个题外的,记录一下)
1LL是为了在计算时,把int类型的变量转化为long long,然后再赋值给long long类型的变量。不至于后面计算溢出,* 1LL之后类型就转换为long long,在进行类型转换的时候,在其他类型的数字后面乘以一个1LL,就可以避免强制转换时候的精度问题