LG-P5607
题解区不让交了,遂来博客园写一发。
感觉和题解区的两种暴力方法一起使用差不多,写了根号分治+在线的做法,理论时间复杂度达到了 \(O(10^9)\),但常数小一点(甚至比那些时间复杂度完全正确的跑的快,拿到了最优解)。
两种暴力方法:
1.每次询问直接比较,暴力求出其并集大小,并算出交集。
2.维护 \(S_{i,j}\) 表示集合 \(i\) 和集合 \(j\) 的交集大小,并用 Bitset 维护一个集合里有哪些数,添加时进行实时维护。
易发现第一种暴力方法适合较小的集合,第二种方法适合数量少的集合。
那么根号分治,大小小于 \(B\) 的集合用第一种,大小大于 \(B\) 的集合用第二种,一旦过程中发现某个集合通过添加大于 \(B\), 则为其添加一个 Bitset 并计算与其余大集合的答案,加入\(S\)。
(\(B\) 的值进行了略微调整,在实际数据中能跑得更快。)
CODE
#include<bits/stdc++.h>
#define fst first
#define sec second
#define mkp(a,b) make_pair(a,b)
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int maxn=1e6+5,B=400;
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read(){
int x=0,f=1;
char ch=nc();
while(ch<48||ch>57)
{
if(ch=='-')
f=-1;
ch=nc();
}
while(ch>=48&&ch<=57)
x=x*10+ch-48,ch=nc();
return x*f;
}
void write(int x){
if(x<0)
putchar('-'),x=-x;
if(x>9)
write(x/10);
putchar(x%10+'0');
return;
}
bitset<maxn> bt[2505];
int s[2505][2505];
int tg[maxn],rg[2505],tot=0;
vector<int> v[maxn];
bool t[maxn];
int sz[maxn];
int main(){
int m; m=read();
while(m--){
int op,a,b;
op=read(),a=read(),b=read();
if(op==1){
v[a].push_back(b),++sz[a];
if(sz[a]==B){
++tot,tg[a]=tot,rg[tot]=a;
for(auto i : v[a]){
bt[tot][i]=1;
}
for(int i=1;i<tot;i++){
s[i][tot]=sz[rg[i]]+sz[a];
for(auto j : v[a]){
s[i][tot]-=bt[i][j];
}
}
}
else if(sz[a]>B){
bt[tg[a]][b]=1;
int tga=tg[a];
for(int i=1;i<tg[a];i++){
s[i][tga]+=(!bt[i][b]);
}
for(int i=tg[a]+1;i<=tot;i++){
s[tga][i]+=(!bt[i][b]);
}
}
}
else{
if(sz[a]<sz[b]) swap(a,b);
int ans=sz[a]+sz[b];
int tga=tg[a];
if(sz[a]<B){
for(auto i : v[b]) t[i]=1;
for(auto i : v[a]) ans-=t[i];
for(auto i : v[b]) t[i]=0;
}
else if(sz[b]<B){
for(auto i : v[b]) ans-=bt[tga][i];
}
else{
if(tg[a]>tg[b]) swap(a,b);
ans=s[tg[a]][tg[b]];
if(tg[a]==tg[b]) ans=sz[a];
}
write(ans); putchar('\n');
}
}
return 0;
}
//^o^

浙公网安备 33010602011771号