Codeforces 1468M. Similar Sets
题目大意
给定 \(n(2\le n\le10^5)\) 个集合,每个集合大小为 \(k_i(2\le k_i\le10^5,\sum k\le2\cdot10^5)\) ,集合内每个元素 \(1\le a_{ij}\le10^9\) 。求一对集合,其交集大小至少为 \(2\) 。
思路
考虑根号分治,我们先进行离散化,之后对于 \(k_i>\sqrt{\sum k}\) 的集合,将所有元素放入桶里维护,接着我们可以枚举其他所有集合,检查是否存在一个集合有两个元素与之相同即可,待检查的集合数量不超过 \(\sqrt{\sum k}\) ,于是这部分复杂度 \(O(\sum k\sqrt{\sum k})\) 。
在考虑另一部分集合,我们可以预处理出每个集合中所有出现的二元组,最后只要检查是否存在两个相同的二元组即可。这些集合的二元组数量总和为 \(\sum{\frac{k(k-1)}{2}}\) ,将所有二元组按第一维建桶,最后检查时扫描每个桶看是否存在两个相同的第二维即可,此外还要记录每个二元组所属的集合,最后这一部分的复杂度也为 \(O(\sum k\sqrt{\sum k})\) 。
代码
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;
const int maxm = 200010;
int T, N, M, K[maxn];
vector<int>A[maxn];
int tota = 0, totb = 0;
int a[maxn], b[maxn];
int vis[maxm];
vector<PII>B[maxm];
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
void write(int x)
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int compress()
{
vector<int>v;
for (int i = 1; i <= N; i++)
{
for (auto& x : A[i])
v.push_back(x);
}
sort(all(v));
v.erase(unique(all(v)), v.end());
for (int i = 1; i <= N; i++)
{
for (auto& x : A[i])
x = upper_bound(all(v), x) - v.begin();
}
return v.size();
}
void solve()
{
tota = totb = 0;
int sum = 0;
for (int i = 1; i <= N; i++)
{
K[i] = read() , sum += K[i];
int num;
for (int j = 1; j <= K[i]; j++)
num = read(), A[i].push_back(num);
}
M = compress();
int base = sqrt(sum) / 2;
for (int i = 1; i <= N; i++)
{
if (K[i] > base)
a[++tota] = i;
else
b[++totb] = i;
}
for (int c = 1; c <= tota; c++)
{
for (int k = 0; k < K[a[c]]; k++)
vis[A[a[c]][k]] = 1;
for (int i = 1; i <= N; i++)
{
if (i == a[c])
continue;
int cnt = 0;
for (int j = 0; j < K[i]; j++)
{
cnt += vis[A[i][j]];
if (cnt >= 2)
{
printf("%d %d\n", a[c], i);
for (int k = 0; k < K[a[c]]; k++)
vis[A[a[c]][k]] = 0;
return;
}
}
}
for (int k = 0; k < K[a[c]]; k++)
vis[A[a[c]][k]] = 0;
}
for (int i = 1; i <= M; i++)
B[i].clear();
for (int i = 1; i <= totb; i++)
{
for (int j = 0; j < K[b[i]]; j++)
{
for (int k = j + 1; k < K[b[i]]; k++)
{
int x = A[b[i]][j], y = A[b[i]][k];
if (x > y)
swap(x, y);
B[x].push_back(PII(y, b[i]));
}
}
}
for (int i = 1; i <= M; i++)
{
if (B[i].size() < 2)
continue;
for (int z = 0; z < B[i].size(); z++)
{
if (!vis[B[i][z].first])
vis[B[i][z].first] = B[i][z].second;
else
{
printf("%d %d\n", vis[B[i][z].first], B[i][z].second);
for (int k = 0; k < B[i].size(); k++)
vis[B[i][k].first] = 0;
return;
}
}
for (int k = 0; k < B[i].size(); k++)
vis[B[i][k].first] = 0;
}
puts("-1");
}
int main()
{
T = read();
while (T--)
{
N = read();
for (int i = 1; i <= N; i++)
A[i].clear();
solve();
}
return 0;
}