CF1468M Similar Sets 题解
考虑根号分治。设 \(m=\sum k_i\),则把 \(k>\sqrt m\) 的称为大集合,\(k\leq \sqrt m\) 的称为小集合。
对于小集合与小集合:
暴力枚举每个集合的每一对 \((x,y)\),开一个哈希表维护有没有其他的集合也存在这个数对。由于集合大小不超过 \(\sqrt m\),复杂度可以证明最大是 \(O(m\sqrt m)\)。
对于大集合和其他集合:
枚举大集合的所有数放到哈希表里,暴力扫其他每个集合,判断是否相似。由于大集合的个数最多 \(O(\sqrt m)\),所以这部分复杂度是 \(O(m\sqrt m)\) 的,然后就做完了。
点击查看代码
#pragma GCC optimize(2)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<cmath>
#include<unordered_map>
#include<algorithm>
#define mp std::make_pair
#define fi first
#define se second
typedef std::pair<int,int> pii;
inline int rd(){
int res=0;char c=getchar();
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())res=(res<<1)+(res<<3)+(c-'0');
return res;
}
const int K=150;
const int N=2e5+13;
int n,m,len[N],small[N],big[N],c[N],from[N];
std::vector<int> a[N];
int ms[1][N];
std::vector<pii> vis[N];
/*struct HashTable//map int to T
{
#define T int//int can be replaced by the class you want
#define T_0 0//0 can be replaced by the zero item of T
inline void swap(int *&x,int *&y){ int *t=x;x=y;y=t; }
inline void swap(bool *&x,bool *&y){ bool *t=x;x=y;y=t; }
inline unsigned long long xor64(){ static unsigned long long x=19260817; return x^=x<<13,x^=x>>17,x^=x<<7; }
inline bool is_prime(int x){ if(x==1) return 0; for(int i=2;i*i<=x;i++) if(x%i==0) return 0; return 1; }
inline int abs(int x){ return x<0?-x:x; }
//inline void swap(T *&x,T *&y){ T *t=x;x=y;y=t; }//if T is not int, this should be used
int *a;bool *used;
T *b;
int m,c1,c2,c3,c4;
int size;
inline HashTable(){ a=NULL,b=NULL,used=NULL; }
inline void clear(){ if(a) delete[] a,delete[] b,delete[] used,a=NULL,b=NULL,used=NULL; size=0; }
inline int hash(const int &x,const int &t){ return ((long long)c1*x+c2+t*(((long long)c3*x%m+c4)%(m-1)+1))%m; }
inline int _insert(int x,T y){ int p=hash(x,0),t=0; while(used[p]&&a[p]!=x) p=hash(x,++t); if(a[p]==x) return -p; used[p]=1,a[p]=x,b[p]=y; return p; }
inline void resize(int n){ int last=m;m=n+!(n&1); while(!is_prime(m)) m+=2; int *ta=new int[m];T *tb=new T[m];bool *tused=new bool[m]; memset(tused,0,sizeof(bool)*m);swap(ta,a),swap(tb,b),swap(tused,used); c1=xor64()%(m-1)+1,c2=xor64()%(m-1)+1,c3=xor64()%(m-1)+1,c4=xor64()%(m-1)+1; for(int i=0;i<last;i++) if(tused[i]) _insert(ta[i],tb[i]); if(ta!=NULL) delete []ta,delete []tb,delete []tused; }
//insert: negtive integer means key exists, and its abs is the position; find: -1 means key doesn't exist; erase: 0 means successful erased, -1 means key doesn't exist
inline int insert(int x,T y){ if((size+1)*2+1>m) resize((size*2+1)*2+1); int p=hash(x,0),t=0; while(used[p]&&a[p]!=x) p=hash(x,++t); if(a[p]==x) return -p; used[p]=1,a[p]=x,b[p]=y,size++; return p; }
inline int find(int x){ int p=hash(x,0),t=0; while(used[p]&&a[p]!=x) p=hash(x,++t); if(a[p]==x) return p; return -1; }
inline int erase(int p){ return find(p)==-1?-1:used[find(p)]=0; }
inline int& operator [] (int x){ return b[abs(insert(x,T_0))]; }
#undef T
#undef T_0
}ms[N];*/
int main(){
int T=rd();while(T--){
n=rd();m=0;int cntt=0;
for(int i=1;i<=n;++i) a[i].clear();
for(int i=1;i<=n;++i){
len[i]=rd();m+=len[i];
for(int j=1;j<=len[i];++j){
int x=rd();
a[i].push_back(x);
c[++cntt]=x;
}
}
std::sort(c+1,c+cntt+1);cntt=std::unique(c+1,c+cntt+1)-(c+1);
for(int i=1;i<=n;++i)
for(int j=0;j<len[i];++j) a[i][j]=std::lower_bound(c+1,c+cntt+1,a[i][j])-c;
int ls=0,lb=0;
for(int i=1;i<=n;++i){
if(len[i]<=K) small[++ls]=i;
else big[++lb]=i;
}
bool game_over=0;int ansx=0,ansy=0;
for(int i=1;i<=cntt;++i) vis[i].clear();
for(int i=1;i<=ls;++i){
int p=small[i];
for(int j=0;j<len[p];++j){
for(int k=j+1;k<len[p];++k){
int x=a[p][j],y=a[p][k];
if(x>y) std::swap(x,y);
vis[x].push_back(mp(y,p));
}
}
}
for(int i=1;i<=cntt;++i){
for(auto x:vis[i]){
if(from[x.fi]){game_over=1;ansx=from[x.fi],ansy=x.se;break;}
from[x.fi]=x.se;
}
for(auto x:vis[i]) from[x.fi]=0;
if(game_over) break;
}
if(game_over){printf("%d %d\n",ansx,ansy);continue;}
for(int i=1;i<=lb;++i){
int p=big[i];
for(int j=1;j<=cntt;++j) ms[0][j]=0;
for(int j=0;j<len[p];++j) ms[0][a[p][j]]=1;
for(int j=i+1;j<=lb;++j){
int q=big[j],cnt=0;
for(int k=0;k<len[q];++k){
cnt+=ms[0][a[q][k]];
if(cnt>=2){game_over=1;ansx=p,ansy=q;break;}
}
if(game_over) break;
}
if(game_over) break;
for(int j=1;j<=ls;++j){
int q=small[j],cnt=0;
for(int k=0;k<len[q];++k){
cnt+=ms[0][a[q][k]];
if(cnt>=2){game_over=1;ansx=p,ansy=q;break;}
}
if(game_over) break;
}
if(game_over) break;
}
if(game_over){printf("%d %d\n",ansx,ansy);continue;}
puts("-1");
}
return 0;
}
//M