数据结构进阶
并查集
一些基本的操作
get(int x)
{
if(fa[x] == x) return x;
return fa[x] = get(fa[x])
}
merge(int x,int y)
{
fa[get(x)] = get(y);
return;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n;
int fa[N * 2];
map<int ,int >num;
int cnt;
vector<pair<int ,int > >deny;
int get(int x)
{
if(fa[x] == x) return x;
return fa[x] = get(fa[x]);
}
void merge(int x,int y)
{
fa[get(x)] = get(y);
}
int main()
{
int T;
cin >> T;
while(T)
{
deny.clear();
num.clear();
T --;
cin >> n;
cnt = 0;
int a,b,op;
for(int i = 1;i <= N * 2;i ++) fa[i] = i;
for(int i = 1;i <= n;i ++)
{
scanf("%d%d%d",&a,&b,&op);
if(num.find(a) == num.end()) num[a] = ++ cnt;
if(num.find(b) == num.end()) num[b] = ++ cnt;//离散化
int aa = num[a],bb = num[b];
// cout << aa << " " << bb << endl;
if(op == 1)
merge(aa,bb);
else
deny.push_back({aa,bb});
}
bool flag = 1;
for(int i = 0;i < deny.size();i ++)
{
if(get(deny[i].first) == get(deny[i].second))
{
printf("NO\n");flag = 0;break;
}
}
if(flag)
printf("YES\n");
}
return 0;
}
下面这种手动离散化会快一点
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n;
int fa[N * 2];
int figure[N * 2];
int cnt;
struct{
int a,b,op;
}num[N];
int get(int x)
{
if(fa[x] == x) return x;
return fa[x] = get(fa[x]);
}
void merge(int x,int y)
{
fa[get(x)] = get(y);
}
int main()
{
int T;
cin >> T;
while(T)
{
T --;
cin >> n;
// cnt = 0;
int a,b,op;
for(int i = 1;i <= N * 2;i ++) fa[i] = i;
for(int i = 1;i <= n;i ++)
scanf("%d%d%d",&num[i].a,&num[i].b,&num[i].op),figure[i] = num[i].a,figure[i + n] = num[i].b;
sort(figure + 1,figure + 2 * n + 1);
cnt = unique(figure + 1,figure + 2 * n + 1) - (figure + 1);
for(int i = 1;i <= n;i ++)
{
if(num[i].op == 1)
{
int aa = lower_bound(figure + 1,figure + cnt + 1,num[i].a) - figure;
int bb = lower_bound(figure + 1,figure + cnt + 1,num[i].b) - figure;
merge(aa,bb);
}
}
bool flag = 1;
for(int i = 1;i <= n;i ++)
{
if(num[i].op == 0)
{
int aa = lower_bound(figure + 1,figure + cnt + 1,num[i].a) - figure;
int bb = lower_bound(figure + 1,figure + cnt + 1,num[i].b) - figure;
if(get(aa) == get(bb))
{
printf("NO\n");flag = 0;break;
}
}
}
if(flag)
printf("YES\n");
}
return 0;
}
并查集主要用来判断两个元素是否属于同一类,广泛一点可以推到判断一种关系
首先分析题意,如果说l~r之间的数有偶数个,那么前l - 1和前r个数的奇偶性相等。奇数个则相反。
然后我们可以得到,x1和x2奇偶性相同,x2和x3奇偶性相同,那么x1和x3奇偶性也相同。
x1和x2奇偶性不同,x2和x3奇偶性相同,x1和x3奇偶性也不同
以此类推,就可以类比异或算法,0 ^ 0 = 0(两组奇偶性相同),1 ^ 1(两组的奇偶性都不相同) = 0,0 ^ 1(两组的奇偶性相反) = 1
所以这个有传递性
边带权算法
边带权,就是把每条边的距离设为0或1,分别代表相同和不同.
当发现x和y不属于同一个集合时,合并时注意根之间的长度
x和y属于同一个集合时,判断他们分别到根节点的异或值与给出的是否对应.
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
struct {
int l,r,ans;
}query[N];
int a[N * 2],fa[N * 2],d[N * 2],n,m,t;
void read_discrete()//读入并离散化
{
cin >> n >> m;
for(int i = 1;i <= m;i ++)
{
char str[5];
scanf("%d%d%s",&query[i].l,&query[i].r,str);
if(str[0] == 'o') query[i].ans = 1;
else query[i].ans = 0;
a[++ t] = query[i].l - 1;
a[++ t] = query[i].r;
}
sort(a + 1,a + t + 1);
n = unique(a + 1,a + t + 1) - a - 1;
}
int get(int x)
{
if(x == fa[x]) return x;
int root = get(fa[x]);
d[x] ^= d[fa[x]];
return fa[x] = root;
}
int main()
{
read_discrete();
for(int i = 1;i <= n;i ++) fa[i] = i;
for(int i = 1;i <= m;i ++)
{
int x = lower_bound(a + 1,a + n + 1,query[i].l - 1) - a;
int y = lower_bound(a + 1,a + n + 1,query[i].r) - a;
int p = get(x),q = get(y);
if(p == q)
{
if((d[x] ^ d[y]) != query[i].ans)
{
cout << i - 1;
return 0;
}
}
else
{
fa[p] = q;
d[p] = d[x] ^ d[y] ^ query[i].ans;//这里异或两个点到根的值和奇偶关系值
}
}
cout << m;
return 0;
}
扩展域算法
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
struct {
int l,r,ans;
}query[N];
int a[N * 2],fa[N * 2 + 10],n,m,t;
void read_discrete()
{
cin >> n >> m;
for(int i = 1;i <= m;i ++)
{
char str[5];
scanf("%d%d%s",&query[i].l,&query[i].r,str);
if(str[0] == 'o') query[i].ans = 1;
else query[i].ans = 0;
a[++ t] = query[i].l - 1;
a[++ t] = query[i].r;
}
sort(a + 1,a + t + 1);
n = unique(a + 1,a + t + 1) - a - 1;
}
int get(int x)
{
if(x == fa[x]) return x;
return get(fa[x]);
}
void merge(int x,int y)
{
fa[get(x)] = get(y);
return ;
}
int main()
{
read_discrete();
for(int i = 1;i <= n;i ++) fa[i] = i,fa[i + N] = i + N;
for(int i = 1;i <= m;i ++)
{
int x = lower_bound(a + 1,a + n + 1,query[i].l - 1) - a;
int y = lower_bound(a + 1,a + n + 1,query[i].r) - a;
if(query[i].ans == 1)
{
if(get(x) == get(y))
{
cout << i - 1;
return 0;
}
merge(x + N , y);
merge(x,y + N);
}
else
{
if(get(x + N) == get(y))
{
cout << i - 1;
return 0;
}
merge(x , y);
merge(x + N,y + N);
}
}
cout << m;
return 0;
}
树状数组
#include<bits/stdc++.h>
using namespace std;
const int N = 5 * 1e5 + 2;
int n,m;
int c[N];
int a[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x,int i)
{
for(;x <= n;x += lowbit(x)) c[x] += i;
}//添加值
int get(int l,int r)
{
int sum = 0;
for(;l;l -= lowbit(l)) sum -= c[l];
for(;r;r -= lowbit(r)) sum += c[r];
return sum;
}//得到区间值
int main()
{
cin >> n >> m;
for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
for(int i = 1;i <= n;i ++)
{
c[i] += a[i];
if(i + lowbit(i) <= n)
c[i + lowbit(i)] += c[i];
}//初始化树状数组
for(int i = 1;i <= m;i ++)
{
int d,a,b;
scanf("%d%d%d",&d,&a,&b);
if(d == 1)
add(a,b);
else
printf("%d\n",get(a - 1,b));
}
}
#include<bits/stdc++.h>
using namespace std;
const int N = 500009;
int n,m;
int c[N];
int a[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x,int i)
{
for(;x <= n;x += lowbit(x)) c[x] += i;
}
int get(int x)
{
int sum = 0;
for(;x;x -= lowbit(x)) sum += c[x];
return sum;
}
int op,x,y,k;
int main()
{
cin >> n >> m;
for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
for(int i = 1;i <= m;i ++)
{
scanf("%d",&op);
if(op == 1)
{
scanf("%d%d%d",&x,&y,&k);
add(x,k);
add(y + 1,-k);
}
else
{
scanf("%d",&k);
printf("%d\n",a[k] + get(k));
}
}
}