题目链接:http://codeforces.com/problemset/problem/506/D
题目大意:
给出n个顶点,m条边,每条边上有一个数字,代表某个颜色。不同数字代表不同的颜色。有很多个询问,每个询问问有多少条纯种颜色的路径使得某两个点联通。
分析:
这个题一看就想用并查集来搞,每种颜色用一个并查集处理。对于输入的每条边,我们只需要将这两个点在这条边的颜色对应的并查集中合并就好了。查询的时候,我们只需要检测这两个点在多少个颜色对应的并查集中是在统一集合中的,那么就有多少条纯种颜色的路径使得两个点联通了。
看起来好像很简单呢,但是我们注意到这个题的数据范围,不管是顶点,还是颜色,还是询问,还是边数,都是10^5的数量级的,那么这么一股脑的敲出来肯定是不行的。
首先,最大的问题就是,题目中有多少中颜色就得有多少个并查集,每个并查集也得有n的顶点的容量,在最大数据的情况下,这样的数组是万万开不下来的。。。那该怎么办?
这里我们可以先将所有输入的边存下来,按照颜色值排一下序,对每一种颜色集中处理,将这种颜色需要的信息提炼出来,在遇到下一种颜色的时候,上一个颜色的并查集就没有用,可以继续使用这个并查集的空间来存储。这样的话,我们的并查集在查询的时候就用不了了,只能离线将答案保存好。查询的时候o(1)输出。
离线将答案保存的方法,由于查询的是一对点,我们就只能用map建立pair到int的映射了。
好像这样就可以了呢!真的是这样吗?我们想想,在对每一种颜色集中处理过程中,需要找出查询中有的,并且在这一种颜色的并查集中属于同一集合的点对找出来。这是比较难的工作。做法是这样的,将这一颜色代表的图中的顶点存下来,然后两层for循环暴力遍历每个点对,查找满足条件的点对。这种做法在颜色比较多,每一种颜色中的点不多的时候是不会超时的,但是如果在每种颜色代表的图中含有的点比较多的话,这样的两层for循环就会超时了。。。无奈只好设置一个阀值,如果某种颜色中的点数小于这个阈值,那就两层for循环暴力扫。如果大于这个阈值,那就直接遍历所有询问,检测需要查询的所有点对即可。
于是。。。我用了一种极其丑陋的姿势过了这题。。T.T
代码:
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <map> using namespace std; #define N 100010 struct Info{ int u,v,c; bool operator<(const Info o) const { return c<o.c; } }info[N]; map<pair<int,int>,int> ans; vector<pair<int,int> > uniQ; struct query{ pair<int,int> q; }Q[N]; int par[N]; int n,m; bool vervis[N]; void init(int* par,int n) { for(int i=1;i<=n;i++) { par[i]=i; } } int find(int* par,int x) { if(x==par[x]) return x; return par[x]=find(par,par[x]); } void Union(int* par,int u,int v) { u=find(par,u); v=find(par,v); if(u==v) return ; par[v]=u; } int main() { #ifndef ONLINE_JUDGE //freopen("E:/in.txt","r",stdin); //freopen("E:/my.txt","w",stdout); #endif while(~scanf("%d%d",&n,&m)) { for(int i=0;i<m;i++) { int u,v,c; scanf("%d%d%d",&u,&v,&c); info[i].u=u,info[i].v=v,info[i].c=c; } sort(info,info+m); init(par,n); int qnum; scanf("%d",&qnum); for(int i=0;i<qnum;i++) { int u,v; scanf("%d%d",&u,&v); Q[i].q.first=min(u,v),Q[i].q.second=max(u,v); uniQ.push_back(Q[i].q); ans[Q[i].q]=0; } sort(uniQ.begin(),uniQ.end()); uniQ.resize(unique(uniQ.begin(),uniQ.end())-uniQ.begin()); vector<int> ver; for(int i=0;i<m;i++) { int u=info[i].u,v=info[i].v,c=info[i].c; Union(par,u,v); if(!vervis[u]) ver.push_back(u); if(!vervis[v]) ver.push_back(v); vervis[u]=1,vervis[v]=1; if(info[i+1].c!=info[i].c) { if(ver.size()*ver.size()>uniQ.size()) { for(int j=0;j<uniQ.size();j++) { int au=uniQ[j].first; int av=uniQ[j].second; if(find(par,au)==find(par,av)) ans[uniQ[j]]++; } } else { for(int j=0;j<ver.size();j++) { int au=ver[j]; for(int k=j+1;k<ver.size();k++) { int av=ver[k]; if(find(par,au)==find(par,av)&&ans.count(make_pair(min(au,av),max(au,av)))) ans[make_pair(min(au,av),max(au,av))]++; } } } if(i!=m-1){ for(int j=0;j<ver.size();j++) { par[ver[j]]=ver[j]; vervis[ver[j]]=0; } ver.clear(); } } } for(int i=0;i<qnum;i++) { cout<<ans[Q[i].q]<<endl; } } return 0; }
Codeforces Round #286 div.1 D 506D D. Mr. Kitayuta's Colorful Graph【并查集】