并查集区间染色问题

2023C-任务处理
在某个项目中有多个任务(用\(tasks\)数组表示)需要您进行处理,其中\(tasks[i] = [s_i, e_i]\),你可以在\(s_i<= day<=e_i\)中的任意一天处理该任务。请返回你可以处理的最大任务数。
注:一天可以完成一个任务的处理。

输入
第一行为任务数量\(n\)\(1 <= n <= 100000\)
后面n行表示各个任务的开始时间和终止时间,用\(s_i\)\(e_i\)表示,\(1 <= s_i <= e_i <= 100000\)

输出
输出为一个整数,表示可以处理的最大任务数。

样例输入
3
1 1
1 2
1 3
样例输出
3

这个题首先是一个贪心,我们对这些任务要按照结束时间进行排序。然后我们在用并查集区间染色,解决:
并查集的区间染色:
比如:
1 3
pre[1]=1,pre[2]=2,pre[3]=3
假设我们将把1这个任务完成了,之后我们将pre[1]=2,这样我们的pre[i]的含义就是i之后第一个未处理的任务

#include<iostream>
#include<algorithm>
#include<cstring> 
typedef long long ll;
using namespace std;
const int maxn=1e5+100;
int pre[maxn];
struct node{
    ll s;
    ll e;
}a[maxn];
bool cmp(node x,node y){
    return x.e<y.e;
}
int find(int x){
    if(pre[x]==x){
        return x;
    }    
    else{
        return pre[x]=find(pre[x]);
    }
}
int main(){
	int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a[i].s,&a[i].e);
    } 
    for(int i=1;i<maxn;i++){
    	pre[i]=i;
	}
    sort(a+1,a+n+1,cmp);
    int ans=0;
    for(int i=1;i<=n;i++){
    	int t=find(a[i].s);
    	if(t<=a[i].e){
    		ans++;
    		pre[t]=find(t+1);
		}
	}
	cout<<ans<<endl;
}

疯狂的馒头

CQF 十分喜欢吃馒头。

兴奋之下他一下子买了\(N\)个馒头请所有认识他的人吃。

但是 CQF 不喜欢白色,喜欢红色、黄色、绿色等鲜艳的颜色。

于是他把所有白色的馒头排成一列。

然后进行\(M\)次染色操作。

每个染色操作都是用一个神奇的刷子把连续的多个馒头染成特定的某种颜色。

一个馒头最终的颜色是最后一次染它的颜色。

如果一个馒头没有被染过色,那么它的颜色就是白色。

现在 CQF 已经定好了染色计划:在第\(i\)次染色操作中,把第 \((i×p+q)modN+1\)个馒头和第 \((i×q+p)modN+1\)个馒头之间的馒头染成颜色\(i\),其中 \(p\),\(q\) 是特定的两个正整数。

他想立即知道最后每个馒头的颜色。

你能帮他吗?

输入格式
第一行四个正整数 \(N,M,p,q\)

输出格式
一共输出 \(N\) 行,第 \(i\) 行表示第 \(i\) 个馒头的最终颜色(如果最终颜色是白色就输出 \(0\))。

数据范围
在 20% 的数据中,\(1≤N≤10^3,1≤M≤10^4\)
在 40% 的数据中,\(1≤N≤10^4,1≤M≤10^5\)
在 60% 的数据中,\(1≤N≤5×10^4,1≤M≤5×10^5\)
在 80% 的数据中,\(1≤N≤3×10^5,1≤M≤3×10^6\)
在 100% 的数据中,\(1≤N≤10^6,1≤M≤10^7\)
保证所有输入数据中 \(1≤M×p+q,M×q+p≤2^{31}−1。\)

输入样例:
4 3 2 4
输出样例:
2
2
3
0

这个题是一个经典的并查集区间染色问题
这种类型的题也可以用树状数组或者线段树写
那么怎么样用并查集写呢?
我们考虑倒着覆盖,就是如果倒着这个点染过色了,就不要再染了,
那么这个pre数组就代表的它的祖先的右边的第一个没有染色的节点
就是这样如果i这个点染色了,就让pre[i]=i+1,这样路径压缩一下就很快

#include<iostream>
#include<algorithm>
using namespace std;
const int maxx=1e6+100; 
int pre[maxx];
int find(int x){
    if(pre[x]==x){
        return pre[x];
    }
    else{
        return pre[x]=find(pre[x]);
    }
}
int ans[maxx];
int main(){
    int n,m,p,q;
    cin>>n>>m>>p>>q;
    for(int i=0;i<=n+10;i++){
        pre[i]=i;
    } 
    int ql,qr,r,l;
    for(int i=m;i>=1;i--){
        ql=(i*p+q)%n+1,qr=(i*q+p)%n+1;
        l=min(ql,qr);
        r=max(ql,qr);
        while(find(l)<=r){
            l=find(l);
            ans[l]=i;
            pre[l]=l+1;
        }
    } 
    for(int i=1;i<=n;i++){
        cout<<ans[i]<<"\n"; 
    }
}

包含每个查询的最小区间

给你一个二维整数数组\(intervals\),其中\(intervals[i] = [left_i, right_i]\)表示第\(i\)个区间开始于\(lefti\)、结束于\(righti\)(包含两侧取值,闭区间)。区间的 长度 定义为区间中包含的整数数目,更正式地表达是\(righti - lefti + 1\)

再给你一个整数数组\(queries\)。第\(j\)个查询的答案是满足\(left_i <= queries[j] <= right_i\)的 长度最小区间 i 的长度 。如果不存在这样的区间,那么答案是 -1 。

以数组形式返回对应查询的所有答案。

示例 1:

输入:intervals = [[1,4],[2,4],[3,6],[4,4]], queries = [2,3,4,5]
输出:[3,3,1,4]
解释:查询处理如下:

  • Query = 2 :区间 [2,4] 是包含 2 的最小区间,答案为 4 - 2 + 1 = 3 。
  • Query = 3 :区间 [2,4] 是包含 3 的最小区间,答案为 4 - 2 + 1 = 3 。
  • Query = 4 :区间 [4,4] 是包含 4 的最小区间,答案为 4 - 4 + 1 = 1 。
  • Query = 5 :区间 [3,6] 是包含 5 的最小区间,答案为 6 - 3 + 1 = 4 。
    示例 2:

输入:intervals = [[2,3],[2,5],[1,8],[20,25]], queries = [2,19,5,22]
输出:[2,-1,4,6]
解释:查询处理如下:

  • Query = 2 :区间 [2,3] 是包含 2 的最小区间,答案为 3 - 2 + 1 = 2 。
  • Query = 19:不存在包含 19 的区间,答案为 -1 。
  • Query = 5 :区间 [2,5] 是包含 5 的最小区间,答案为 5 - 2 + 1 = 4 。
  • Query = 22:区间 [20,25] 是包含 22 的最小区间,答案为 25 - 20 + 1 = 6 。

提示:
\(1 <= intervals.length <= 10^5\)
\(1 <= queries.length <= 10^5 \)intervals[i].length == 2$
\(1 <= lefti <= righti <= 10^7\)
\(1 <= queries[j] <= 10^7\)

这个题的题意就是,给你一些区间[l,r],
就是问你包含点x的区间的并且长度最小的区间长度
这个题和上一个是一样的,就是这个按照区间长度排序,让区间长度小的先覆盖

class Solution {
public:
    vector<int>xs,p,w;
    int find(int x){
        if(p[x]!=x){
            return p[x]=find(p[x]);
        }
        return p[x];
    }
    int get(int x){
        return lower_bound(xs.begin(),xs.end(),x)-xs.begin();
    }
    vector<int> minInterval(vector<vector<int>>& segs, vector<int>& queries) {
        for(auto &s: segs) xs.push_back(s[0]),xs.push_back(s[1]);
        for(auto x:queries) xs.push_back(x);
        sort(xs.begin(),xs.end());
        xs.erase(unique(xs.begin(), xs.end()), xs.end());
        int n=xs.size();
        p.resize(n+1);
        w.resize(n+1,-1);
        for(int i=0;i<=n;i++){
            p[i]=i;
        }
        sort(segs.begin(), segs.end(), [](vector<int>& a, vector<int>& b) {
            return a[1] - a[0] < b[1] - b[0];
        });//这个是按照长度从小到大排序
        for(auto& s:segs){
            int l=get(s[0]),r=get(s[1]),len=s[1]-s[0]+1;
            while(find(l)<=r){
                l=find(l);
                w[l]=len;
                p[l]=l+1;
            }
        }
        vector<int>ans;
        for(auto x: queries) ans.push_back(w[get(x)]);
        return ans;
    }
};
posted @ 2024-04-26 16:29  lipu123  阅读(151)  评论(0)    收藏  举报