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;
}