2020寒假并查集训练赛(C、D、G)

C题: Destroying Array codeforces 772C

题意:给出一个长度为n的序列,每次删除一个(删除之后序列断开),求最大连续子段和。

思路:可以逆向来考虑,每次添加一个数,只有可能把他左边或右边的那两个数连接起来,所以用一个标记来判断相邻的两个数是否在之前已被添加,如果被添加,用并查集把这两个点合并,然后更新最大值。

附上代码:

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stdlib.h>
#include<set> 
#include<vector>
#include<queue> 
#include<map>
using namespace std;
const int maxn = 1e5+10;
const int inf = 0x3f3f3f3f;
typedef long long  ll;
const int mod=1e4+7;
//#define int long long;
const double eps=1e-6;
inline int read(){
    
     
   int s=0,w=1;  
   char ch=getchar();  
   while(ch<='0'||ch>'9'){
    
    if(ch=='-')w=-1;ch=getchar();}  
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();  
   return s*w;  
}
int n;
ll fa[maxn],vis[maxn],sum[maxn],ans[maxn];
ll num[maxn];//原序列 
ll creat[maxn];//删除数 
int find(int x){
    
    
	return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
void baba(int x,int y){
    
    
	int fx=find(x),fy=find(y);
	if(fx!=fy){
    
    
		fa[fx]=fy;
	    sum[fy]+=sum[fx];//更新连续子段和 
	}
}
int main(){
    
    
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
    
    
		fa[i]=i;
		scanf("%d",&num[i]);
	}
	for(int i=1;i<=n;i++){
    
    
		scanf("%d",&creat[i]);
	}
	ans[n]=0;//删完数必定为0 
	for(int i=n;i>=1;i--){
    
    
		int temp=creat[i];
		sum[temp]=num[temp];
		vis[temp]=1;
		if(temp!=1&&vis[temp-1]){
    
    //判断左边是否有数 
			baba(temp,temp-1);
		}
		if(temp!=n&&vis[temp+1]){
    
    //判断右边是否有数 
			baba(temp,temp+1);
		}
		ans[i-1]=max(ans[i],sum[find(temp)]);//取出最大值 
	}
	for(int i=1;i<=n;i++){
    
    
		printf("%lld\n",ans[i]);
	}
}

D题: Chemical table codeforecs 1013D

题意:有一块n*m的区域,其中有q个格子带有标记,如下图所示,若有(r1,c1),(r1,c2),(r2,c1),那么(r2,c2)就可以自动生成标记,问若最后所有的格子都被标记,还需要的最小标记数。在这里插入图片描述
思路:若原区域中没有标记,那么我们最少需要放n+m-1个标记,如下图所示。之后就是等效替换放进去的q个标记,这时就可以用到并查集。
在这里插入图片描述
附上代码:

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stdlib.h>
#include<set> 
#include<vector>
#include<queue> 
#include<map>
using namespace std;
const int maxn = 4e5+10;
const int inf = 0x3f3f3f3f;
typedef long long  ll;
const int mod=1e4+7;
//#define int long long;
const double eps=1e-6;
inline int read(){
    
     
   int s=0,w=1;  
   char ch=getchar();  
   while(ch<='0'||ch>'9'){
    
    if(ch=='-')w=-1;ch=getchar();}  
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();  
   return s*w;  
}
int n,m,q;
int fa[maxn];
int ans;
int find(int x){
    
    
    return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
void baba(int x,int y){
    
    
	int fx=find(x),fy=find(y);
	if(fx!=fy){
    
    //可以等效替换标记 ,ans--; 
		fa[fx]=fy;
		ans--;
	}
} 
int main(){
    
    
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n+m;i++){
    
    
    	fa[i]=i;
	}
    ans=n+m-1;//最多需要标记的数量 
    while(q--){
    
    
        int x,y;
        scanf("%d%d",&x,&y);
        baba(x,y+n);
    }
    printf("%d\n",ans);
}

G题:ZJnu stadium HDU 3047

题意:给两个数n,m分别代表n个人,m对关系: 下面输入的就是这m对关系,a,b,x;表示的是a,b相距x个距离;然后判断输入的是否与这个数的之前的数信息一致, 输出不一致的总数;
题解:用一个sum数组记录他到他的祖先的距离,接下来就是用带权并查集公式推导求解,要注意方向。

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stdlib.h>
#include<set> 
#include<vector>
#include<queue> 
#include<map>
using namespace std;
const int maxn = 5e4+10;
const int inf = 0x3f3f3f3f;
typedef long long  ll;
//const int mod=1e4+7;
//#define int long long;
const double eps=1e-6;
inline int read(){
    
     
   int s=0,w=1;  
   char ch=getchar();  
   while(ch<='0'||ch>'9'){
    
    if(ch=='-')w=-1;ch=getchar();}  
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();  
   return s*w;  
}
int fa[maxn];
int sum[maxn];//点到根节点的距离 
int find(int x){
    
    
	if(fa[x]!=x){
    
    
		int temp=fa[x];
		fa[x]=find(fa[x]);
	    sum[x]+=sum[temp];
	}
	return fa[x];
}
int main(){
    
    
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF){
    
    
		int ans=0;
		for(int i=1;i<=n;i++){
    
    
			fa[i]=i;
			sum[i]=0;
		}
		while(m--){
    
    
			int a,b,s;
			scanf("%d%d%d",&a,&b,&s);
			int fx=find(a),fy=find(b);
            if(fx==fy){
    
    //若之前有关系 
                if(sum[a]-sum[b]!=s) ans++; //判断是否与之前的关系相悖 
            }
            else{
    
    //若之前没有关系,则建立新的关系 
                fa[fx] = fy;
                sum[fx] = s-sum[a]+sum[b];
            }
		}
		printf("%d\n",ans);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_43633353/article/details/103957849