CF506D Mr. Kitayuta's Colorful Graph
题面传送门
看到数颜色就可以想到要用分块。
然后把正解想出来然后复杂度算错
然后发现我们有两种方法:
第一种,将每个颜色都建成并查集然后直接查询,这个是\(O(nq)\)的。
第二种,将每种颜色内的暴力匹配扔到map里,然后对于每个询问查询。这个是\(O(n^2logn+qlogn)\)的。
然后想到将两个结合一下。
设阈值\(K\),大于\(k\)个的颜色是\(O(\frac{n}{K}q)\)的。小与的分析一下是\(O(nKlognlogK)\)的。
然后\(K\)取\(\frac{\sqrt n}{logn}\)时可以达到\(O(n\sqrt nlogn)\)
但是这个东西我们发现如果我们事先把每个询问扔进去然后后面那个只有一个log
那么\(K=\sqrt {\frac{n}{logn}}\)平衡一下复杂度就可以变成\(O(n\sqrt{nlogn})\)
然后你会发现map常数太大了。所以\(K\)大一点会表现好一点。
code:
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#define N 100039
#define I inline
using namespace std;
int n,m,k,q,x[N],y[N],z[N],st[N],en[N],fa[N],now,un,wn,fl[N],ans[N];
map<int,int> flag[N],f[N];vector<int>c[N],g[N];
I int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
I void make(int x){
register int i,j,now1,now2;
for(i=0;i<g[x].size();i++){
for(j=i+1;j<g[x].size();j++) now1=g[x][i],now2=g[x][j],flag[now1].count(now2)&&(f[now1][now2]++),flag[now2].count(now1)&&(f[now2][now1]++);
} g[x].clear();
}
int main(){
freopen("1.in","r",stdin);
register int i,j;scanf("%d%d",&n,&m);k=sqrt(m);for(i=1;i<=n;i++) fa[i]=i;
for(i=1;i<=m;i++) scanf("%d%d%d",&x[i],&y[i],&z[i]),c[z[i]].push_back(i);
scanf("%d",&q);for(i=1;i<=q;i++) scanf("%d%d",&st[i],&en[i]),flag[st[i]][en[i]]=1;;
for(i=1;i<=m;i++){
if(c[i].size()<=k){
for(j=0;j<c[i].size();j++){
now=c[i][j];un=find(x[now]);wn=find(y[now]);
if(un==wn) continue;fa[un]=wn;
}
for(j=0;j<c[i].size();j++) now=c[i][j],!fl[x[now]]&&(g[find(x[now])].push_back(x[now]),fl[x[now]]=1),!fl[y[now]]&&(g[find(y[now])].push_back(y[now]),fl[y[now]]=1);
for(j=0;j<c[i].size();j++)now=c[i][j],make(find(x[now]));
for(j=0;j<c[i].size();j++) now=c[i][j],fa[x[now]]=x[now],fa[y[now]]=y[now],fl[x[now]]=0,fl[y[now]]=0;
}
else{
for(j=0;j<c[i].size();j++){
now=c[i][j];un=find(x[now]);wn=find(y[now]);
if(un==wn) continue;fa[un]=wn;//printf("%d %d\n",un,wn);
}
for(j=1;j<=q;j++) ans[j]+=(find(st[j])==find(en[j]));
for(j=1;j<=n;j++) fa[j]=j;//printf("\n");
}
}
for(i=1;i<=q;i++)printf("%d\n",ans[i]+f[st[i]][en[i]]);
}