[KSN2021] Binary Land
题目描述
给定一张 N N N 个点的图,每个点有权值 A i A_i Ai 和价值 B i B_i Bi。
两个点 x , y x,y x,y 之间存在一条无向边当且仅当 A x xor A y > max ( A x , A y ) A_x\text{ xor }A_y>\max(A_x,A_y) Ax xor Ay>max(Ax,Ay)。
你需要对于 i = 1 , 2 , ⋯ n i=1,2,\cdots n i=1,2,⋯n 依次求出点 i i i 所在连通块中所有点的价值和。
输入格式
第一行输入一个正整数 N N N。
第二行输入 N N N 个正整数 A i A_i Ai。
第三行输入 N N N 个正整数 B i B_i Bi。
输出格式
输出 N N N 行,第 i i i 行包含一个整数,代表点 i i i 所在连通块中所有点的价值和。
样例 #1
样例输入 #1
3
2 1 1
20 30 10
样例输出 #1
60
60
60
样例 #2
样例输入 #2
4
5 4 4 5
10 20 30 40
样例输出 #2
10
20
30
40
样例 #3
样例输入 #3
5
1 2 1 7 11
20 10 30 100 100
样例输出 #3
60
60
60
200
200
提示
【数据范围】
本题采用捆绑测试。
- Subtask 1(8 points):只存在一组数据,满足 N = 8 N=8 N=8, A = [ 6 , 39 , 11 , 63 , 3 , 39 , 1 , 43 ] A=[6,39,11,63,3,39,1,43] A=[6,39,11,63,3,39,1,43], B = [ 4 , 8 , 3 , 7 , 9 , 1 , 2 , 2 ] B=[4,8,3,7,9,1,2,2] B=[4,8,3,7,9,1,2,2]。
- Subtask 2(13 points):保证 N ≤ 200 N \leq 200 N≤200。
- Subtask 3(10 points):保证 N ≤ 2000 N \leq 2000 N≤2000。
- Subtask 4(4 points):保证 A 1 = A 2 = ⋯ = A n A_1=A_2=\cdots=A_n A1=A2=⋯=An。
- Subtask 5(7 points):保证存在非负整数 k k k 使得 A i = 2 k A_i=2^k Ai=2k。
- Subtask 6(19 points): A i ≤ 2 12 − 1 A_i\leq 2^{12}-1 Ai≤212−1。
- Subtask 7(39 points):无特殊限制。
对于所有数据, 1 ≤ N ≤ 1 0 5 1 \leq N \leq 10^5 1≤N≤105, 1 ≤ A i ≤ 2 30 − 1 1 \leq A_i \leq 2^{30}-1 1≤Ai≤230−1, 1 ≤ B i ≤ 1 0 9 1 \leq B_i \leq 10^9 1≤Bi≤109。
#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <cmath>
#define ll long long
using namespace std;
const int maxn=1e6+10;
ll f[maxn],sum[maxn],a[maxn];
bool vis1[maxn],vis2[maxn];
int n;
int find(int x)
{
if(x!=f[x]) f[x]=find(f[x]);
return f[x];
}
void merge(int u,int v)
{
int x,y;
x=find(u),y=find(v);
if (x==y) return ;
f[x]=y;//把x街道以y为根的树上去
sum[y]+=sum[x];//值也要加到根上去,后面查询每个联通量,就直接查询根的值即可
return ;
}
int main ()
{
scanf ("%d",&n);
for (int i=1;i<=n;i++)
{
scanf ("%lld",&a[i]);
}
for (int i=1;i<=n;i++)
{
scanf ("%lld",&sum[i]);
}
for (int i=1;i<=n*2;i++)//注意,是1到n*2,因为它后面会用到tmp+n+1,它肯定比n大
{
f[i]=i;
}
for (int i=1;i<=n;i++)
{
int tmp=(int)log2(a[i]);
vis1[tmp]=true;
for (int j=tmp-1;j>=0;j--)
{
if (!(a[i]>>j&1))
{
vis2[j]=true;
}
}
}
for (int i=1;i<=n;i++)
{
int tmp=(int)log2(a[i]);
if (!vis1[tmp])
{
continue;
}
merge(tmp+n+1,i);//让第个作为那个较小的,tmp+n+1,是为了不让干扰后面可能会出现的以为他们已经并在一起了,下同
}
for (int i=1;i<=n;i++)//让第i个作为那个较大的
{
int tmp=(int)log2(a[i]);
for (int j=tmp-1;j>=0;j--)
{
if (!(a[i]>>j&1))//有情况
{
if (!vis1[j])//看有没有在较大的1后面的位置为第一个1的数,如果没有,continue
{
continue;//不存在符合这个条件的,下一个
}
merge(j+1+n,i);//如果较大的数绑上了第j位,并且还有较小的数绑上了第j位,那么较大的数就是和较小的数绑在一起了
}
}
}
for (int i=1;i<=n;i++)
{
printf ("%lld\n",sum[find(i)]);
}
system("pause");
return 0;
}
本人还是个小蒟蒻,如有问题,请斧正!