当我们会碰到这样一类问题时,长度为n的序列,m个询问(通常n和m同阶)。可能存在两种比较显然的方法,一种是O(n^2)预处理O(1)回答,一种是不预处理O(n)回答m个询问,当然这两种方法都是O(n2)的。这时候我们就可以考虑根号分治,将这两种算法糅合在一起,达到一个nsqrt(n)的效果。
题目:
给定n个集合,输出是否存在两个集合,他们至少有两种元素相同,集合总数不超过2e5。
分析:
对于这个题目,可以想到两种暴力的方法。1.对于每个集合,暴力查找别的集合是否与其匹配。2.预处理出所有的二元对,查看他们能否在同一相同的集合。但是这两种方法都是平方级别的。
我们将其结合在一起,如果一个集合的大小大于了总元素个数的根号,那个处理这个集合我们选用方法1,反正最多也就根号级别的数量个集合,每次暴力判复杂度为O(msqrt(m))。对于那些小于sqrt(m)的集合,我们用方法2,将他们的二元对全部处理出来做判断即可,那这些二元对最多就m*sqrt(m)对,复杂度也是O(msqrt(m))。
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;
const int maxn = 2e5+5;
vector<int> s[maxn],v;
vector<pair<int,int> > g[maxn];
int ans1,ans2;
int vis[maxn];
void solve(int n,int m)
{
int siz = sqrt(m);
for (int i = 1; i <= n; i++)
{
if( s[i].size() < siz )
{
for (int j = 0; j < s[i].size(); j++)
for (int k = j+1; k < s[i].size(); k++)
{
if( s[i][j] > s[i][k] )
g[s[i][k]].push_back(make_pair(s[i][j],i));
else g[s[i][j]].push_back(make_pair(s[i][k],i));
}
}else
{
for (int j = 0; j < s[i].size(); j++) vis[s[i][j]] = 1;
for (int j = 1; j <= n; j++)
{
if( i == j ) continue;
int cnt = 0;
for (int k = 0; k < s[j].size(); k++)
{
if( vis[s[j][k]] ) cnt ++;
}
if( cnt >= 2 )
{
ans1 = i,ans2 = j;
return;
}
}
for (int j = 0; j < s[i].size(); j++) vis[s[i][j]] = 0;
}
}
for (int i = 1; i <= v.size(); i++)
{
for (int j = 0; j < g[i].size(); j++)
{
pair<int,int> p = g[i][j];
if( vis[p.first] != 0 )
{
ans1 = vis[p.first];
ans2 = p.second;
return;
}else vis[p.first] = p.second;
}
for (int j = 0; j < g[i].size(); j++) vis[g[i][j].first] = 0;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin >> t;
while( t-- )
{
int n,m = 0;
cin >> n;
for (int i = 1; i <= n; i++)
{
int k;
cin >> k;
for (int j = 1; j <= k; j++)
{
int x;
cin >> x;
v.push_back(x);
s[i].push_back(x);
}
m += k;
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < s[i].size(); j++)
{
s[i][j] = lower_bound(v.begin(),v.end(),s[i][j]) - v.begin() + 1;
}
}
ans1 = ans2 = -1;
solve(n,m);
if( ans1 == -1 ) cout << -1 << '\n';
else cout << ans1 << ' ' << ans2 << '\n';
for (int i = 1; i <= n; i++) s[i].clear();
for (int i = 1; i <= m; i++) g[i].clear(),vis[i] = 0;
v.clear();
}
return 0;
}