http://www.lydsy.com/JudgeOnline/problem.php?id=1854
题意:n个数据,每个数据有两个属性,要求取一些数据且每个数据取一个属性使得组成连续的一段单调递增的数(从1开始),求最大能连续到多少。(n<=1000000)
#include <bits/stdc++.h> using namespace std; const int N=1000005; int p[N], n, mx; bool vis[N]; int ifind(int x) { return x==p[x]?x:p[x]=ifind(p[x]); } struct dat { int x, y; }a[N]; int main() { scanf("%d", &n); for(int i=0; i<n; ++i) scanf("%d %d", &a[i].x, &a[i].y), mx=max(mx, max(a[i].x, a[i].y)); for(int i=1; i<=mx; ++i) p[i]=i; for(int i=0; i<n; ++i) { int fx=ifind(a[i].x), fy=ifind(a[i].y); if(fx>fy) swap(fx, fy); if(fx==fy) vis[fx]=1; else { vis[fx]=1; p[fx]=fy; } } for(int i=1; i<=mx+1; ++i) if(!vis[i]) { printf("%d ", i-1); return 0; } return 0; }
一开始我想了个很诡异的贪心...wa了...然后自测是90囧QAQ....(然后对着数据改程序然后rp好我改了一个地方就a了....
然后明摆着我那样做是错的....请看当时代码:
#include <bits/stdc++.h> using namespace std; const int N=1000005; int cnt[N], n, mx, ihead[N], tot; struct dat { int next, to; }e[N]; inline void add(const int &u, const int &v) { e[++tot].next=ihead[u]; ihead[u]=tot; e[tot].to=v; } int main() { scanf("%d", &n); for(int i=0; i<n; ++i) { int x, y; scanf("%d %d", &x, &y); if(x>y) swap(x, y); mx=max(mx, y); ++cnt[x]; ++cnt[y]; add(x, y); } for(int i=1; i<=mx+1; ++i) { if(cnt[i]==0) { printf("%d ", i-1); break; } int cmx=mx+2; for(int j=ihead[i]; j; j=e[j].next) if(cnt[e[j].to]==1) cmx=min(cmx, e[j].to); --cnt[cmx]; //printf("%d ", cmx); --cnt[i]; } return 0; }
。。。。。
后来查了题解。。。好神的并查集....
首先我们可以将每个点所有的两个属性看做两个点,然后连边。
那么有:
如果每个连通块是一棵树,大小为a,显然a-1个数一定能得到
如果连通块是环,大小为a,那么a个数都能得到
然后就用并查集维护,且启发式合并:小的数设置大的数为父亲,而且默认每个连通块的根的vis为0.除非有环,则连通块的根的vis为1