Day12总结

· 2026-7-23 12:59:18

T1: 题目:原序列中,c[i]=i.现在要求在序列上支持两种操作:

1 x c:将与x相邻且颜色相同的单元格绘制为颜色c。

2 C:打印颜色c的单元格数。

分析:已经合并的,不会再次分开。所以这和并查集的性质一样,用并查集解决,维护每段的左右L[i],R[i],以及col[i]和sz[i].

/**/
#include<bits/stdc++.h>
using namespace std;
int fa[500005];
int sz[500005];
int col[500005];
int L[500005];
int R[500005];
int ans[500005];
int q;
int n;
int find(int x){
	if(fa[x]==x)return x;
	else return fa[x]=find(fa[x]); 
}
int main(){
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		col[i]=i;
		sz[i]=1;
		fa[i]=i;
        L[i]=i;
        R[i]=i;
        ans[i]=1;
	}
while(q--){
	int op;
	cin>>op;
	if(op==1){
		int x,c;
		cin>>x>>c;
        int fx=find(x);
        ans[col[fx]]-=(R[fx]-L[fx]+1);
        ans[c]+=(R[fx]-L[fx]+1);
        col[fx]=c;
        int l=L[fx]-1;
        int r=R[fx]+1;
        if(l>=1){
            int fl=find(l);
            if(col[fl]==col[fx]){
                fa[fl]=fa[fx];
                sz[fx]+=sz[fl];
                L[fx]=L[fl];
            }
        }
        if(r<=n){
            int fr=find(r);
            if(col[fr]==col[fx]){
                fa[fr]=fa[fx];
                sz[fx]+=sz[fr];
                R[fx]=R[fr];
            }
        }

	} else{
        int c;
        cin>>c;
        cout<<ans[c]<<"\n";
	}
}
	return 0;
}

T2:

题目分析:并查集模版和求集合大小

#include<bits/stdc++.h>
using namespace std;
int n,m;
int fa[500005];
int sz[500005];
int find(int x){
	if(fa[x]==x)return x;
	else return fa[x]=find(fa[x]);
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		fa[i]=i;
		sz[i]=1;
	}
	for(int i=1;i<=m;i++){
		int k;
		cin>>k;
		int x;
        if(k==0)continue;
		k-=1;
		cin>>x;

		while(k--){
            if(k<0)break;
			int y;
			cin>>y;
			int fx=find(x);
			int fy=find(y);
			fa[fy]=fx;
            if(fx!=fy)
            sz[fx]+=sz[fy];
		} 
	}
	for(int i=1;i<=n;i++){
		cout<<sz[find(i)]<<" ";
	}
	return 0;
}

T3: 题目:给一个序列,将数组中的数按照这个序列给出的顺序一个个拿走,问每次拿走后最大的连续段和是多少。

分析:如果按照题目给出的顺序,难以处理。可以考虑倒着处理,一个个加入数组中,更好维护连续段的和。

/*
一个个删除=反向一个个加入
倒着维护各个集合内元素总和以及集合 
*/
#include<bits/stdc++.h>
using namespace std;
int n; 
int a[100005];
long long sum[100005];
int fa[100005];
int era[100005];
long long ans[100005];
long long maxx=0;
bool vis[100005];
int find(int x){
    if(fa[x]==x)return x;
    else return fa[x]=find(fa[x]);
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=n;i++){cin>>a[i];sum[i]=a[i];}
	for(int i=1;i<=n;i++)cin>>era[i];
	for(int j=n;j>=1;j--){
		int i=era[j];
		vis[i]=1;
        int fi=find(i);
        ans[j]=maxx;
        maxx=max(maxx,sum[fi]);
		if(vis[i-1]==1&&vis[i+1]!=1){
			int f1=find(i-1);
			fa[fi]=f1;
			if(fi!=f1){
                sum[f1]+=sum[fi];
		    	maxx=max(maxx,sum[f1]);
            } 
        }
		if(vis[i+1]==1&&vis[i-1]!=1){
			int f1=find(i+1);
			fa[fi]=f1;
            if(fi!=f1){
	    		sum[f1]+=sum[fi];
    			maxx=max(maxx,sum[f1]);
            } 
        }
        if(vis[i+1]==1&&vis[i-1]==1){
            int f1=find(i-1);
            int f2=find(i+1);
            fa[f1]=fi;
            fa[f2]=fi;
            sum[fi]=sum[fi]+sum[f1]+sum[f2];
            maxx=max(maxx,sum[fi]);
        }
        
	}
	for(int i=1;i<=n;i++)cout<<ans[i]<<"\n"; 
	return 0;
}

T4: 题目:给定一张图。每个联通块内的节点编号必须连续。若不连续,可以与其他联通块连边,问最少要联多少条边才能满足上面的条件? 思路:用并查集做。维护每个集合的最大最小值。最后按照最小值排序。如果下一个集合的最小值<上一个集合的最大值,ans++; 如果不小于了,那么要新开一个联通块。

#include<bits/stdc++.h>
using namespace std;
int n,m;
int fa[200005];
struct node{
    int l,r;
}h[200005];
vector<node>v;
vector<int>lian[200005];
int bianhao[200005];
int cnt=0;
int find(int x){
    if(fa[x]==x)return x;
    else return fa[x]=find(fa[x]);
}
bool cmp(node a,node b){
    return a.l<b.l;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		fa[i]=i;
        h[i].l=i;
        h[i].r=i;
	}
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		int fu=find(u);
		int fv=find(v);
        if(fu>fv)swap(fu,fv);
    	fa[fu]=fv;
        h[fv].l=min(h[fu].l,h[fv].l);
        h[fv].r=max(h[fu].r,h[fv].r);
    }
    for(int i=1;i<=n;i++){
        if(fa[i]==i){
            v.push_back({h[i].l,h[i].r});
        }
    }
    sort(v.begin(),v.end(),cmp);
    int now_r=v[0].r;
    int ans=0;
    if(v.empty()){
        cout<<ans<<"\n";
        return 0;
    }
    for(int i=1;i<(int)v.size();i++){
        int nxt_l=v[i].l;
        int nxt_r=v[i].r;
        if(nxt_l<=now_r){
            ans++;
            now_r=max(now_r,nxt_r);
        } else{
            now_r=nxt_r;
        }
    }
    cout<<ans;
	return 0;
}

T5: 题目: 给定一张含 n 个顶点、m 条带权边的连通无向图,其中有 k 个特殊顶点。 一条路径的代价定义为路径上最大的边权。两个顶点的距离,定义为连接它们的所有路径中的最小代价。 对每个特殊顶点,求它到其他特殊顶点的最大距离。 分析:k个点两两互通,就是求这k个点形成的树。由于求最大距离,所以将边按边权从小到大排序,如果形成了树,那么就求得了答案。由于要求最大距离,所以最大的边就是答案。

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int is[100005];
struct Edge{
	int u,v,w;
}e[100005];
bool cmp(Edge a,Edge b){
	return a.w<b.w;
}
int fa[100005];
int sz[100005];
int find(int x){
	if(fa[x]==x)return x;
	else return fa[x]=find(fa[x]);
}
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	for(int i=1;i<=k;i++){
		int x;
		cin>>x;
		is[x]=1;
        sz[x]=1;
	}
	for(int i=1;i<=m;i++){
		cin>>e[i].u>>e[i].v>>e[i].w;
	}
	sort(e+1,e+1+m,cmp);
	int ans=0;
	for(int i=1;i<=m;i++){
		int u=e[i].u;
		int v=e[i].v;
        int ru=find(u);
        int rv=find(v);
		if(find(u)!=find(v)){
			fa[ru]=rv;
            sz[rv]+=sz[ru];
		}
		if(sz[rv]==k){
			ans=e[i].w;
			break;
		}
	}
	while(k--){
		cout<<ans<<" ";
	}
	return 0;
} 

T6: 分析:对边按权值从小到大排序,对限制条件从小到大排序,在限制条件下尽可能加边,再判断边上的两点是否不在同一集合中,若不在,则更新ans=ans+sz[u]*sz[v].

#include<bits/stdc++.h>
using namespace std;
int n,m;
int fa[200005],sz[200005];
struct node{
	int u,v,w;
}e[200005];
struct node1{
	int id;
	int val;
    long long an=0;
}q[200005];
bool cmp(node x,node y){
	return x.w<y.w;
}
bool cmp2(node1 x,node1 y){
    return x.val<y.val;
}
bool cmp1(node1 x,node1 y){
    return x.id<y.id;
}
int find(int x){
    if(x==fa[x])return x;
    else return fa[x]=find(fa[x]);
}
unsigned long long ans=0;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		fa[i]=i;
		sz[i]=1; 
	}
	for(int i=1;i<=n-1;i++)cin>>e[i].u>>e[i].v>>e[i].w;
	for(int i=1;i<=m;i++){
		cin>>q[i].val;
		q[i].id=i;
	}
    sort(e+1,e+n,cmp);
    sort(q+1,q+1+m,cmp2);
	int i=1,j=1;
	for(;j<=m;j++){
		while(e[i].w<=q[j].val&&i<=n-1){
			int u=e[i].u;
			int v=e[i].v;
			u=find(u);
			v=find(v);
			if(u!=v){
				if(u>v)swap(u,v);
				fa[u]=v;
				ans+=(long long)sz[u]*sz[v];
				sz[v]+=sz[u];
			}
            i++;
		} 
        q[j].an=ans;
	}
    sort(q+1,q+1+m,cmp1);
    for(int i=1;i<=m;i++)cout<<q[i].an<<" ";
	return 0;
}
/*
 
*/
1 次查看 举报

0 条评论

目前还没有评论...

Be the first to comment!

返回讨论列表
徐廷蔚
107
通过题目
10
发帖数