题解:P5607 [Ynoi2013] 无力回天 NOI2017
先把两个集合的并的大小换成两个集合大小之和减交的大小,问题变成如何快速查询两个集合的交。我们有两种暴力,一种是直接做集合的交,另一种是从元素对查询的贡献入手,维护拥有某个元素的集合的集合,维护一张
第一种暴力可以用 bitset 优化,第二种暴力,设最终拥有元素
次。考虑对两种暴力根号分治平衡复杂度,对
然后还有些问题,首先 bitset 开太多空间要炸(没有炸太多),但是由于所有 bitset 中为
如果被卡常记得调一调块长。下面给个参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXM=1e6,S=100,B=(MAXM-1)/(S+1)+1,SIZE=MAXM*S/2+2*MAXM;
int Read()
{
int res=0;char c;
while(!isdigit(c=getchar()));
while(isdigit(c)) res=res*10+c-'0',c=getchar();
return res;
}
void Print(int x)
{
if(x<10) {putchar('0'+x);return;}
int y=x/10;
Print(y);
putchar('0'+x-y*10);
}
int m,opt[MAXM+5],X[MAXM+5],Y[MAXM+5];
bitset<B> Set[MAXM/2+5];
int SID[MAXM+5],Single[MAXM+5],snum;
vector<int> Q[MAXM+5];
int buc[MAXM+5],Code[MAXM+5],tot;
int cnt[MAXM+5];
int ans[MAXM+5],qry[SIZE+5],lst[MAXM+5];
int Cap(int a,int b)
{
if(!SID[a] || !SID[b]) return 0;
if(SID[a]>0 && SID[b]>0) return (Set[SID[a]]&Set[SID[b]]).count();
if(SID[a]>0) return Set[SID[a]][Single[b]];
if(SID[b]>0) return Set[SID[b]][Single[a]];
return Single[a]==Single[b];
}
int main()
{
m=Read();
for(int i=1;i<=m;i++)
{
opt[i]=Read(),X[i]=Read(),Y[i]=Read();
if(opt[i]==1) ++buc[Y[i]];
}
for(int i=1;i<=m;i++) if(buc[i]>S) Code[i]=tot++;
for(int i=1;i<=m;i++)
if(opt[i]==1) {if(buc[Y[i]]<=S) lst[X[i]]+=cnt[Y[i]]++;}
else if(X[i]!=Y[i]) ++lst[X[i]],++lst[Y[i]];
for(int i=1;i<=m;i++) lst[i]+=lst[i-1];
for(int i=m;i;i--) cnt[i]=0,lst[i]=lst[i-1];
for(int i=1;i<=m;i++)
if(opt[i]==1)
{
++cnt[X[i]];
if(buc[Y[i]]<=S)
{
for(int j=0;j<Q[Y[i]].size();j++) qry[++lst[X[i]]]=Q[Y[i]][j];
Q[Y[i]].push_back(X[i]);
}
else
{
if(!SID[X[i]]) {Single[X[i]]=Code[Y[i]],SID[X[i]]=-1;continue;}
if(SID[X[i]]<0) Set[SID[X[i]]=++snum][Single[X[i]]]=1;
Set[SID[X[i]]][Code[Y[i]]]=1;
}
}
else
{
if(X[i]==Y[i]) {ans[i]=cnt[X[i]];continue;}
ans[i]=cnt[X[i]]+cnt[Y[i]]-Cap(X[i],Y[i]);
qry[++lst[X[i]]]=qry[++lst[Y[i]]]=-i;
}
for(int i=1;i<=m;i++) cnt[i]=0;
for(int i=1;i<=m;i++)
{
for(int j=lst[i-1]+1;j<=lst[i];j++)
if(qry[j]>0) ++cnt[qry[j]];
else
{
int v=-qry[j];
if(i==X[v]) ans[v]-=cnt[Y[v]];
else ans[v]-=cnt[X[v]];
}
for(int j=lst[i-1]+1;j<=lst[i];j++) if(qry[j]>0) --cnt[qry[j]];
}
for(int i=1;i<=m;i++) if(opt[i]==2) Print(ans[i]),putchar('\n');
return 0;
}