做题小结
前言
这几天身体很差
前天下午做着就感觉心脏很不舒服 然后只好5点回去睡觉了,
然后晚上没吃睡醒了去写题 回去吃了炸鸡 完蛋 第二填早上去机房边写肚子边痛 痛的要死 后面蹲久了
人站起来很不舒服 眼冒金花那种耳朵嗡嗡的那种 就感觉要死了,后面休息了很久才好,果断回寝室休息。
然后晚上去补了那个并查集的题 回寝室玩游戏了 端午节呢
第一个题目 食物链
拓展域并查集
首先要明白 为什么要扩展,其实就是分类,食物链 模板
这道题要开三倍 就是要分敌人 同类 猎物三个对象
然后对于操作1而言
合并同类 我们要分清楚 如果不是同类的时候 那就是x可能是y的食物,也可以x是y的天敌 于是我们就可以find去看看了
对于操作二而言
x吃y 如果是假话 可能x被y吃或者x与y同类
这个很容易理解 但是对于连边我们要注意 如果是真话 我们该怎么连边呢 如果x吃y的话
x是y的天敌
x的猎物是y
x的天敌是y的猎物
其实你会发现 这连来连去很像那个双向链表吗
对的
其实就是连连看 分清楚 关系就行
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int range=5e5+10;
int n;
int a[range];
int k;
int op;
int x,y;
int fa[range*3];
int find(int x)
{
if(x==fa[x])return x;
else {
fa[x]=find(fa[x]);
return fa[x];
}
}
void merge(int x,int y)
{
fa[find(y)]=find(x);
}
void solve()
{
cin>>n;cin>>k;
for(int i=1;i<=3*n;i++)fa[i]=i;
int ans=0;
for(int i=1;i<=k;i++)
{
cin>>op;
cin>>x>>y;
if(x>n||y>n)ans++;
else if(op==1)
{
if(find(x)==find(y+n)||find(x)==find(y+2*n))ans++;
else {
merge(x,y);
merge(x+n,y+n);
merge(x+2*n,y+2*n);
}
}
else {
if(find(x)==find(y)||find(x)==find(y+n))ans++;
else{
merge(x,y+2*n);
merge(x+n,y);
merge(x+2*n,y+n);
}
}
}
cout<<ans<<endl;
return ;
}
signed main()
{
solve();
}
第二道题目题目
这也是一个扩展域并查集的题目

首先我们要明白这个需要开两倍 因为只有两个集合 两个关系 对于一个数 可能会出现两次 那么我们假设其出现时间 说白了就是输入顺序 分别为x.y 那么x+n其实就是y的集合 y+n就是x的集合
如果说 某一时刻 我们发现在输入过程中 两个人同属于一个集合 那肯定是有问题的
举个例子
1 2
4 5
1 3
4 6
2 3
5 6
我们首先对于1进行分析
两个1的输入顺序分别是 1 3
那么我们会连集合
把1+6与3连接 3+6与1连接
此时fa7=3 fa9=1
然后再来到2
输入顺序分别是1 5
找到各自的另一个集合
分别是7 11 然后注意了
fa(find(fa7)=3)=5 于是
fa(3)=5 另一个fa(find(fa11)=11)=1
注意这一步 我们会把fa(3)更新祖先为5 之前fa3=3的
这是因为 fa7 是 1 的对立面, 然后 fa7 =3, 是因为1的第 二 个 1 在 3 , 于是我们更新它 fa 7 = 3 , 现在好了又来个输入顺序也为 1 的 2 , 他和第一个 1 肯定是一个集合的 , 此时它带来了新消息 , 说 fa 7 可以更新为我的对立面 , (第二个 2 在的地方)就是 5 那 , 于是我们 fa (find一下)=5
全部更新为5 路径压缩嘛
然后此时来到3
3的输入顺序分别是3 5 但是此时fa3=5 fa5=5 于是就出现一个集合 于是就完蛋了 明显输出NO 就这么结束了
很妙!并查集!
这道题的思路就讲完了 做法其实很简单的 详见代码
for (int i = 1; i <= n; i++) {
if(v[i].size()==1)continue;
int x = v[i][0];
int y = v[i][1];
int searchx = find(x);
int searchy = find(y);
// cout<<"sssss"<<endl;
// cout<<searchx<<" "<<searchy<<endl;
if (searchx == searchy) {
//cout<<i<<endl;
flag = 1;
break;
}
int xx = find(x + n);
int yy = find(y + n);
fa[xx] = searchy;
fa[yy] = searchx;
}
注意以下 当v数组那个1没有赋值的时候访问会re的 除非你用resize
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int range = 2e5 + 10;
int n;
int fa[range * 2];
vector<int>v[range];
int find(int x) {
if (x == fa[x])return x;
else {
return fa[x] = find(fa[x]);;
}
}
void solve(int t) {
cin >> n;
for (int i = 1; i <= n; i++) {
int a, b;
cin >> a >> b;
v[a].push_back(i);
v[b].push_back(i);}
bool flag = 0;
for (int i = 1; i <= 2 * n; i++)fa[i] = i;
for (int i = 1; i <= n; i++) {
if (v[i].size() > 2)flag = 1;
}
if(flag){
for(int i=1;i<=n;i++)
{
v[i].clear();
}
cout<<"NO"<<endl;
return ;
}
for (int i = 1; i <= n; i++) {
if(v[i].size()==1)continue;
int x = v[i][0];
int y = v[i][1];
int searchx = find(x);
int searchy = find(y);
if (searchx == searchy) {
flag = 1;
break;
}
int xx = find(x + n);
int yy = find(y + n);
fa[xx] = searchy;
fa[yy] = searchx;
}
cout<<(flag?"NO":"YES")<<endl;
下面是另一个题

在这里 完全可以用map直接更新末尾值就行 而我写了个
vector嵌套服了
int n;
int k;
int x,y;
int a[range];
void solve()
{
cin>>n>>k;
map<int,vector<int>>ma;
for(int i=1;i<=n;i++)
{
cin>>a[i];
ma[a[i]].push_back(i);
}
while(k--)
{
cin>>x>>y;
if(ma[x].size()==0||ma[y].size()==0)
{
cout<<"NO"<<endl;
continue;
}
int w=ma[y].size();
if(ma[y][w-1]>=ma[x][0]){
cout<<"YES"<<endl;
}
else cout<<"NO"<<endl;
}
for(int i=1;i<=n;i++)
{
if(ma[a[i]].size())
ma[a[i]].clear();
}
}
开两个map即可
for(i=1;i<=n;++i){
scanf("%d",&x);
if(!l[x])//没见过
l[x]=i;//记录
r[x]=i;//不断更新
}

浙公网安备 33010602011771号