智算之道初赛第二场 第三题 情报战

C.情报战

http://oj.csen.org.cn/contest/6/19

题目描述

现在我方已经查明,敌人通信所使用的加密方式依赖于一个长度为 n 的数列,只要得知了这个数列中每个数的值,我方便可破解敌方的通信。
通过深入敌人内部的内线人员的艰苦奋斗,我方逐渐获得了一些有用的情报,通过这些情报,整个数列正在被不断地破解。
先后有 mm 条情报被得知,每条情报是以下两种情况之一:

  • 情况 1 :知道了数列中第 x 个数的值
  • 情况 2 :知道了数列中第 x 个数和第 y 个数的和

每得知一条情报,我方都试图破解数列中元素的值
作为情报部门核心技术人员的你,请编程实现如下功能:每次得知一条新情报,你都要计算当前已经能够确定出数列中的多少个数了
你比较笨,对于情况 2 这种情报,只能在已知其中一个数的情况下推出另一个数,不能通过若干情况 2 的情报列方程求解

输入格式

第一行,两个正整数 n , m
接下来 m 行,每行的第一个数是 type

  • 如果 type=1 ,则接下来跟着一个整数 x,表示得知了数列中第 x 个数的值;
  • 如果 type=2 ,则接下来跟着两个空格隔开的整数 x , y 表示得知了第 x 个数和第 y 个数的和

输出格式

输出 m 行,每行包含一个非负整数,第 i 行的非负整数表示在得知了前 i 条情报之后数列中已经能够确定的数的数量

数据规模与约定

对于 20% 的数据,1 ≤ n,m ≤ 10 ,且只有第一种情报
对于 50% 的数据, 1 ≤ n , m ≤ 5000
对于 100% 的数据, 1 ≤ n , m ≤ 3 × 105
可能会有重复的情报,也可能出现 x = y 的情况

样例输入

5 4
1 1
1 2
2 2 3
2 1 3

样例输出

2

C题我乍一看以为需要用线段树,结果代码敲到一半想了想可以用变形的并查集,线段树反而没法通过。
所以我在并查集的基础上增加了一个统计他们小弟数量的数列son(读作小弟写作儿子
首先跟并查集一样,找各自的老大,然后找到老大之后,新老大的小弟数量是原本各自的小弟以及其中一个老大,即原先两个老大的数量之和再加一。然后还需要对0进行特判,假设和0相关联,0是永远的老大!!!

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
int a[300005],son[300005];
int find(int k)//找老大
{
	if(a[k]==k)
	{
		return k;
	}
	return a[k]=find(a[k]);
}
int kfind(int k)//归队
{
	if(a[k]==k)
	{
		a[k]=0;
		son[0]+=son[k]+1;
		return 0;
	}
	return a[k]=kfind(a[k]);
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)a[i]=i;
	for(int i=1;i<=m;i++)
	{
		int t,x,y,fx,fy;
		scanf("%d",&t);
		if(t==1)
		{
			scanf("%d",&x);
			fx=find(x);
			if(fx==x)
			{
				son[0]+=son[x]+1;
				a[x]=0;
			}
			else
			{
				if(fx==0)
				{
					printf("%d\n",son[0]);
					continue;
				}
				kfind(x);
			}
		}
		else
		{
			scanf("%d%d",&x,&y);
			fx=find(x);fy=find(y);
			if(fx==fy)
			{
				printf("%d\n",son[0]);
				continue;
			}
			int da,xiao;
			if(fx<fy)
			{
				son[fx]+=son[fy]+1;
				a[fy]=fx;
			}
			else
			{
				son[fy]+=son[fx]+1;
				a[fx]=fy;
			}
		}
		printf("%d\n",son[0]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43813647/article/details/107453775