zoukankan      html  css  js  c++  java
  • kuangbin带你飞 并查集 题解

    做这套题之前一直以为并查集是很简单的数据结构。

    做了才发现自己理解太不深刻。只看重片面的合并集合。。

    重要的时发现每个集合的点与这个根的关系,这个关系可以做太多事情了。

    题解:

    POJ 2236 Wireless Network

    10S时限,尽量做到最优吧。一开始还怕会T

    预处理出能到达的点。简单题看代码就行了

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 1010;
    struct point
    {
        double x,y;
    }src[MAXN];
    int fa[MAXN];
    int Find(int x) {return x == fa[x] ? x : fa[x] = Find(fa[x]);}
    bool repair[MAXN];
    double dis[MAXN][MAXN];
    vector<int>go[MAXN];
    
    int main()
    {
        int N;
        double D;
        scanf("%d%lf",&N,&D);
        for (int i = 1 ; i <= N ; i++) scanf("%lf%lf",&src[i].x,&src[i].y);
        for (int i = 1 ; i <= N ; i++) go[i].clear();
        for (int i = 1 ; i <= N ; i++) fa[i] = i;
        for (int i = 1 ; i <= N ; i++)
        {
            for (int j = i + 1 ; j <= N ; j++)
            {
                dis[i][j] = sqrt((src[i].x - src[j].x) * (src[i].x - src[j].x) + (src[i].y - src[j].y) * (src[i].y - src[j].y));
                dis[j][i] = dis[i][j];
                if (dis[i][j] <= D) go[i].push_back(j);
                if (dis[i][j] <= D) go[j].push_back(i);
            }
        }
    
        memset(repair,false,sizeof(repair));
        char op[5];
        while (scanf("%s",op) != EOF)
        {
            if(op[0] == 'O')
            {
                int x;
                scanf("%d",&x);
                repair[x] = true;
                for (int j = 0 ; j < (int)go[x].size() ; j++)
                {
                    if (repair[go[x][j]])
                    {
                        int fx = Find(x);
                        int fy = Find(go[x][j]);
                        if (fx != fy) fa[fx] = fy;
                    }
                }
            }
            else
            {
                int x,y;
                scanf("%d%d",&x,&y);
                int fx = Find(x);
                int fy = Find(y);
                if (fx == fy) puts("SUCCESS");
                else puts("FAIL");
            }
        }
        return 0;
    }
    View Code


    POJ 1611 The Suspects

    有一个学校,有N个学生,编号为0-N-1,现在0号学生感染了非典,凡是和0在一个社团的人就会感染,并且这些人如果还参加了别的社团,他所在的社团照样全部感染,求感染的人数。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 30010;
    int fa[MAXN];
    int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
    int N,M;
    int main()
    {
        while (scanf("%d%d",&N,&M) != EOF)
        {
            if (N == 0 && M == 0) break;
            for (int i = 0 ; i <= N ; i++) fa[i] = i;
            for (int i = 0 ; i < M ; i++)
            {
                int cnt;
                scanf("%d",&cnt);
                int x;
                scanf("%d",&x);
                int fx = Find(x);
                for (int i = 1 ; i < cnt ; i++)
                {
                    int u;
                    scanf("%d",&u);
                    int fu = Find(u);
                    if (fx != fu) fa[fu] = fx;
                }
            }
            int ret = 1;
            int need = Find(0);
            for (int i = 1 ; i < N ; i++)
            {
                int fi = Find(i);
                if (fi == need) ret++;
            }
            printf("%d
    ",ret);
        }
        return 0;
    }
    View Code

    HDU 1213 How Many Tables
    有几个集合

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 2010;
    int fa[MAXN];
    int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
    int N,M;
    bool vis[MAXN];
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while (T--)
        {
            scanf("%d%d",&N,&M);
            memset(vis,false,sizeof(vis));
            for (int i = 0 ; i <= N ; i++) fa[i] = i;
            while (M--)
            {
                int x,y;
                scanf("%d%d",&x,&y);
                int fx = Find(x);
                int fy = Find(y);
                if (fx != fy) fa[fx] = fy;
            }
            int ret = 0;
            for (int i = 1 ; i <= N ; i++)
                if (i == Find(i)) ret++;
            printf("%d
    ",ret);
        }
        return 0;
    }
    View Code

    HDU 3038 How Many Answers Are Wrong

    第一次接触这类题目,一开始完全没想到要用并查集该怎么做,看了别人的代码才慢慢算是理解?

    重要的是维护当前点,与当前点和他的跟之间的关系。这道题就变成了到他根的和为多少

    我做这道题始终保持跟的下标是其集合内最小的

    这道题的路径压缩是很好理解的。

    重要的是怎么合并的两颗树。也就是unionset代码。看别人的这个代码困扰了我很久。最后自己手动代入才明白了一些

    首先第一保证跟的下标是其集合内最小的 就是代码里要分论讨论的合并fa[x] = y的x和y 的关系

    bool unionset(int x,int y,int val)
    {
        int a = Find(x);
        int b = Find(y);
        if (a == b)
        {
            return sum[y] - sum[x] == val;
        }
        if (a < b)
        {
            fa[b] = a;
            sum[b] = sum[x] + val - sum[y];
        }
        else
        {
            fa[a] = b;
            sum[a] = sum[y] - val - sum[x];
        }
        return true;
    }
    首先sum[x]表示x到他的根的距离
    代码中,对于a > b,在数轴上是这情况。1:b--a--x--y 那么fa[a] = b,就要求最左边那段的长度 就是by - xy - ax = ab
    对于 a < b 在数轴上是这种情况 2:a--b--x--y 或者3: a--x--b--y 对于第一个情况使得fa[b] = a表示最左边的长度就是ax + xy - by = ab及代码中的sum[b] = sum[x] + val - sum[y];
    对于另一种情况 求ab 等于 ax + xy - xy = ab 同样也是那个式子所以可以合并。那么合并集合就可以了
    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 200010;
    int fa[MAXN];
    int sum[MAXN];
    int Find(int x)
    {
        if (x == fa[x]) return x;
        else
        {
            int tmp = fa[x];
            fa[x] = Find(fa[x]);
            sum[x] += sum[tmp];
        }
        return fa[x];
    }
    
    bool unionset(int x,int y,int val)
    {
        int a = Find(x);
        int b = Find(y);
        if (a == b)
        {
            return sum[y] - sum[x] == val;
        }
        if (a < b)
        {
            fa[b] = a;
            sum[b] = sum[x] + val - sum[y];
        }
        else
        {
            fa[a] = b;
            sum[a] = sum[y] - val - sum[x];
        }
        return true;
    }
    
    int main()
    {
        int N,M;
        while (scanf("%d%d",&N,&M) != EOF)
        {
            int ret = 0;
            for (int i = 0 ; i <= N ; i++) fa[i] = i;
            memset(sum,0,sizeof(sum));
            while(M--)
            {
                int x,y,val;
                scanf("%d%d%d",&x,&y,&val);
                if (!unionset(x - 1,y,val)) ret++;
            }
            printf("%d
    ",ret);
        }
        return 0;
    }
    View Code
    POJ 1182 食物链
    这题目是并查集的超经典应用。
    说难起始也难,说不难页不难。。!!

    首先定义结构体。分别是他的父亲。和他与他这题目是并查集的超经典应用。父亲的关系
    relat = 0 表示 其和其父亲是同类
    relat = 1 表示 他的父亲吃他
    relat = 2 表示 他吃他的父亲
    那么初始化就所有值得父亲为他本身,显然他们之间的关系relat = 0本身和本身必然是同类
    对于询问假话,如果2个在一个集合里。通过他们和父亲的关系就可以判断他们之间的relat关系来判断假话
    第一步 : 路径压缩如何更新
    这个你枚举一下就可以发现儿子和爷爷的relat值=(儿子与父亲的relat值+父亲与爷爷的relat值)%3 举个例子。儿子吃父亲 当前relat = 2,爷爷吃父亲,这个的relat = 1,这种情况
    表示儿子和爷爷是同类。所以儿子和爷爷的关系是relat为何只考虑3层,考虑路径压缩压成了几层
    第二步 : 也是我觉得最困扰的如何合并集合。将2个树合并到一起
    先给出代码
    if (roota != rootb)
            {
                p[rootb].fa = roota;
                p[rootb].relat = (op - 1 + 6 + p[a].relat - p[b].relat) % 3;
            }
    当前有a->roota ,b -> rootb a和b之间的op值。根据题意有op = 0,表示2者同类,op - 1是我们预先设置的状态值0,op = 2表示a吃b 。根据我们的设置op - 1 = 1表示父亲吃儿子
    使得rootb的父亲是roota 那么相关的更新你可以这么看 把这4个点拉成一条链(首先我不知道这么做到底对不对。虽然AC了)
    接下来由这张图来看。我们先求rootb和a之间的关系 由前面儿子和爷爷的结论 。我们快速得到这个值等于 (3 - relat[b]) % 3 + (3 - (OP - 1)) % 3
    为什么这样注意:relat[b] 表示b和rootb的关系。不是rootb和b的关系。偏移量值需要改变。同理ab也一样。得到这个值后。这里就可以看做b不存在。三层中有roota--a--rootb
    再利用这个公式就得到 ((3 - relat[b]) % 3 + (3 - (OP - 1)) % 3 + relat[a]) % 3 合并一下同余一下就得到了上面的合并的式子
    最后判断假话
    首先其一定在一个集合内。否则其必然是真话,因为遇到不在一个集合里就可以合并集合
    第一种判断他们是不是同一类生物。这个直接判断和跟relat即可
    另一种判断吃的关系同样也是你手动枚举一下就知道咋弄了。
    代码:
    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 50010;
    struct node
    {
        int fa;
        int relat;
    }p[MAXN];
    
    int Find(int x)
    {
        int tmp;
        if (x == p[x].fa) return x;
        tmp = p[x].fa;
        p[x].fa = Find(p[x].fa);
        p[x].relat = (p[x].relat + p[tmp].relat) % 3;
        return p[x].fa;
    }
    
    int main()
    {
        int N,K;
        int op;
        int ret = 0;
        scanf("%d%d",&N,&K);
        for (int i = 1 ; i <= N ; i++)
        {
            p[i].fa = i;
            p[i].relat = 0;
        }
        for (int i = 1 ; i <= K ; i++)
        {
            int a,b;
            scanf("%d%d%d",&op,&a,&b);
            if(a > N || b > N)
            {
                ret++;
                continue;
            }
            if (op == 2 && a == b)
            {
                ret++;
                continue;
            }
            int roota = Find(a);
            int rootb = Find(b);
            if (roota != rootb)
            {
                p[rootb].fa = roota;
                p[rootb].relat = (op - 1 + 6 + p[a].relat - p[b].relat) % 3;
            }
            else
            {
                if(op == 1 && p[a].relat != p[b].relat)
                {
                    ret++;
                    continue;
                }
                if (op == 2 && (3 - p[a].relat + p[b].relat) % 3 != 1)
                {
                    ret++;
                }
            }
        }
        printf("%d
    ",ret);
        return 0;
    }
    View Code
    POJ 1417 True Liars
    并查集+背包dp
    神只说真话,魔鬼直说假话。那就就发现一个很重要的事情!
    只要回答yes那么2者同类,否则2者异类。这个不解释了
    所以可以用并查集来维护点和根的关系。最后问是否方案数目唯一。于是这个就背包来维护
    这个详情看代码吧
    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 1010;
    int fa[MAXN];
    int val[MAXN];
    bool vis[MAXN];
    int Find(int x)
    {
        if (x == fa[x]) return x;
        int tmp = fa[x];
        fa[x] = Find(fa[x]);
        val[x] = (val[x] + val[tmp]) % 2;
        return fa[x];
    }
    int N,P1,P2;
    int cnt[MAXN][2];
    int dp[MAXN][MAXN];
    int pre[MAXN][MAXN];
    vector<int>ans;
    vector<int>res[MAXN][2];
    
    int main()
    {
        while(scanf("%d%d%d",&N,&P1,&P2) != EOF)
        {
            if (N == 0 && P1 == 0 && P2 == 0) break;
            for (int i = 0 ; i <= P1 + P2 ; i++)
            {
                fa[i] = i;
                val[i] = 0;
            }
            for (int i = 0 ; i < N ; i++)
            {
                int x,y;
                char op[10];
                scanf("%d%d%s",&x,&y,op);
                int fx = Find(x);
                int fy = Find(y);
                if (fx != fy)
                {
                    fa[fy] = fx;
                    int tmp;
                    if (op[0] == 'y') tmp = 0;
                    else tmp = 1;
                    val[fy] = val[x] + val[y] + tmp;
                }
            }
            memset(vis,false,sizeof(vis));
            memset(cnt,0,sizeof(cnt));
            for(int i = 0 ; i <= P1 + P2 ; i++)
            {
                res[i][0].clear();
                res[i][1].clear();
            }
            int cas = 1;
            for (int i = 1 ; i <= P1 + P2 ; i++)
            {
                if (vis[i]) continue;
                int fi = Find(i);//之前写错是因为i这个点不一定是根。我默认这点为跟了。下面就是错误的写法
                /*
                if (vis[i]) continue;
                vis[i] = true;
                int fi = Find(i);
                res[cas][0].push_back(i);
                cnt[cas][0]++;
                for (int j = i + 1 ; j <= P1 + P2 ; j++)
                    */
                for (int j = i ; j <= P1 + P2 ; j++)
                {
                    if (vis[j]) continue;
                    int fj = Find(j);
                    if (fj != fi) continue;
                    vis[j] = true;
                    res[cas][val[j]].push_back(j);
                    cnt[cas][val[j]]++;
                }
                cas++;
            }
            memset(dp,0,sizeof(dp));
            cas--;
           /* for (int i = 1 ; i <= cas ; i++)
            {
                for (int j = 0 ; j < (int)res[i][0].size(); j++)
                    printf("%d ",res[i][0][j]);
                puts("");
                for (int j = 0 ; j < (int)res[i][1].size(); j++)
                    printf("%d ",res[i][1][j]);
                puts("");
                printf("%d %d
    ",cnt[i][0],cnt[i][1]);
            }
            */
            dp[0][0] = 1;
            for (int i = 1 ; i <= cas ; i++)
            {
                for (int j = P1 ; j >= 0 ; j--)
                {
                    if (j >= cnt[i][0] && dp[i - 1][j - cnt[i][0]] > 0)
                    {
                        dp[i][j] += dp[i - 1][j - cnt[i][0]];
                        if (dp[i][j] > 1) dp[i][j] = 2;
                        pre[i][j] = j - cnt[i][0];
                    }
                    if (j >= cnt[i][1] && dp[i - 1][j - cnt[i][1]] > 0)
                    {
                        dp[i][j] += dp[i - 1][j - cnt[i][1]];
                        if (dp[i][j] > 1) dp[i][j] = 2;
                        pre[i][j] = j - cnt[i][1];
                    }
                }
            }
            if (dp[cas][P1] != 1)
            {
                puts("no");
                continue;
            }
            ans.clear();
            int cur = P1;
            for (int i = cas ; i >= 1 ;i--)
            {
                int pref = pre[i][cur];
                int sub = cur - pref;
                if (cnt[i][0] == sub)
                {
                    for (int j = 0 ; j < (int)res[i][0].size(); j++)
                        ans.push_back(res[i][0][j]);
                }
                else
                {
                    for (int j = 0 ; j < (int)res[i][1].size() ; j++)
                        ans.push_back(res[i][1][j]);
                }
                cur = pref;
            }
            sort(ans.begin(),ans.end());
            for (int i = 0 ; i < (int)ans.size() ; i++)
                printf("%d
    ",ans[i]);
            puts("end");
        }
        return 0;
    }
    View Code
    POJ 1456 Supermarket
    这个题数据这么小。怎么做都行。并查集就帮助你理解并查集
    首先这是一个贪心
    肯定优先选择大的完成。。贪心很明显,因为每个物品只需要执行一天,且全部都是一天
    并查集维护点到根之间最近的可用的时间点是什么 ,根下标小与等于树上的点
    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 10010;
    int fa[MAXN];
    int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
    struct node
    {
        int d,p;
        friend bool operator < (const node &a,const node &b)
        {
            return a.p > b.p;
        }
    }src[MAXN];
    int N;
    
    int main()
    {
        while (scanf("%d",&N) != EOF)
        {
            for (int i = 0 ; i < N ; i++) scanf("%d%d",&src[i].p,&src[i].d);
            sort(src,src + N);
            for (int i = 0 ; i < MAXN ; i++) fa[i] = i;
            int ret = 0;
            for (int i = 0 ; i < N ; i++)
            {
                int ti = Find(src[i].d);
                if (ti > 0)
                {
                    ret += src[i].p;
                    fa[ti] = ti - 1;
                }
            }
            printf("%d
    ",ret);
        }
        return 0;
    }
    View Code
    POJ 1733 Parity game
    同样询问假话在哪
    并查集维护点到根之间1的个数是奇数还是偶数。有了前面的铺垫这里就很简单了。
    val = 0 儿子到父亲之间为偶数个1,val = 1 儿子到父亲之间为奇数个1
    第一:路径压缩 : 直接儿子val异或父亲val值。
    第二:合并集合 : 直接全异或。
    首先我保证 父亲的下标小于儿子。对于a,b之间的一个操作(奇数或者偶数)那么可能的情况是 fb--b--a--fa直接求fa到fb的奇偶性,无论什么情况直接val[b] ^ val[a] ^ ab即可对于其他数轴上的情况
    一样的式子
    代码:
    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 10010;
    int fa[MAXN],val[MAXN];
    map<int,int>mp;
    int L;
    int u[MAXN],v[MAXN],ask[MAXN];
    // val = 0  even val = 1 odd
    
    int Find(int x)
    {
        if (x == fa[x]) return x;
        int tmp = fa[x];
        fa[x] = Find(fa[x]);
        val[x] = val[x] ^ val[tmp];
        return fa[x];
    }
    
    int main()
    {
        int N;
        int cas = 1;
        while (scanf("%d",&L) != EOF)
        {
            mp.clear();
            scanf("%d",&N);
            int ans = N;
            for (int i = 0 ; i < N ; i++)
            {
                scanf("%d%d",&u[i],&v[i]);
                if (u[i] > v[i]) swap(u[i],v[i]);
                u[i]--;
                if (!mp[u[i]]) mp[u[i]] = cas++;
                if (!mp[v[i]]) mp[v[i]] = cas++;
                char op[5];
                scanf("%s",op);
                if (op[0] == 'e') ask[i] = 0;
                else ask[i] = 1;
            }
            for (int i = 0 ;i < MAXN ; i++)
            {
                fa[i] = i;
                val[i] = 0;
            }
            for (int i = 0 ; i < N ; i++)
            {
                int l = mp[u[i]];
                int r = mp[v[i]];
                int fl = Find(l);
                int fr = Find(r);
                if (fl == fr)
                {
                    if ((val[l] ^ val[r]) != ask[i])
                    {
                        ans = i;
                        break;
                    }
                }
                else
                {
                    fa[fr] = fl;
                    val[fr] = val[l] ^ val[r] ^ ask[i];
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code
    POJ 1984 Navigation Nightmare
    题很难读懂。这个题卡了我好久。我一直看别人代码十分费解。这个向量偏移,相对值到底该怎么理解
    真是日了X了
    这里都是相对长度,首先只分析南北方向。东西方向一样的分开分析
    第一步:路径压缩:儿子相应长度值+=父亲相应长度值
    第二部: 合并集合: 就这里我百思不得其解。
    下面坐标全部指的是y坐标
    首先保证根的坐标在节点坐标的下方。对于向北(上)方向的直线,认为他是正长度,否则为副长度
    4个点 : r1,r2,f1,f2 r1的根为f1,r2的根为f2
    则有  f2 到 f1 的长度 + r2 到 f2 的长度 =
        r1 到 r2 的长度 + r1 到 f1 的长度 = 向量 r2-f1的长度
    东西方向同理
    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 40010;
    int fa[MAXN];
    int dx[MAXN],dy[MAXN];
    int from[MAXN],to[MAXN],L[MAXN];
    char dir[MAXN][5];
    struct node
    {
        int u,v;
        int index;
        int ti;
        friend bool operator < (const node &a,const node &b)
        {
            return a.ti < b.ti;
        }
    }src[MAXN];
    int ans[MAXN];
    int Find(int x)
    {
        if (x == fa[x]) return x;
        int tmp = fa[x];
        fa[x] = Find(fa[x]);
        dy[x] += dy[tmp];
        dx[x] += dx[tmp];
        return fa[x];
    }
    
    int main()
    {
        int N,M;
        int Q;
        while (scanf("%d%d",&N,&M) != EOF)
        {
            for(int i = 0 ; i <= N ; i++) fa[i] = i;
            memset(dx,0,sizeof(dx));
            memset(dy,0,sizeof(dy));
            for (int i = 1 ; i <= M ; i++)
            {
                scanf("%d%d%d",&from[i],&to[i],&L[i]);
                scanf("%s",dir[i]);
            }
            scanf("%d",&Q);
            for (int i = 0 ; i < Q ; i++)
            {
                scanf("%d%d%d",&src[i].u,&src[i].v,&src[i].ti);
                src[i].index = i;
            }
            sort(src,src + Q);
            int t = 1;
            for (int i = 0 ; i < Q ; i++)
            {
                while (t <= M && src[i].ti >= t)
                {
                    int f1 = Find(from[t]);
                    int f2 = Find(to[t]);
                    if (f1 != f2)
                    {
                        fa[f2] = f1;
                        if (dir[t][0] == 'N')
                        {
                            dy[f2] = L[t] + dy[from[t]] - dy[to[t]];
                            dx[f2] = dx[from[t]] - dx[to[t]];
                        }
                        else if (dir[t][0] == 'S')
                        {
                            dy[f2] = -L[t] + dy[from[t]] - dy[to[t]];
                            dx[f2] = dx[from[t]] - dx[to[t]];
                        }
                        else if (dir[t][0] == 'E')
                        {
                            dx[f2] = L[t] + dx[from[t]] - dx[to[t]];
                            dy[f2] = dy[from[t]] - dy[to[t]];
                        }
                        else
                        {
                            dx[f2] = -L[t] + dx[from[t]] - dx[to[t]];
                            dy[f2] = dy[from[t]] - dy[to[t]];
                        }
                    }
                    t++;
                }
                if (Find(src[i].u) != Find(src[i].v))  ans[src[i].index] = -1;
                else
                {
                    ans[src[i].index] = abs(dx[src[i].u] - dx[src[i].v]) + abs(dy[src[i].u] - dy[src[i].v]);
                }
            }
            for (int i = 0 ; i < Q  ; i++) printf("%d
    ",ans[i]);
        }
        return 0;
    }
    View Code
    POJ 2492 A Bug's Life
    给出交配行为,问是否同性可交配
    默认每一次交配为异性,如果有冲突则满足有同性交配
    同样和父亲的关系为同性 val值为0,否则为1
    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 2010;
    const int MAXD = 1000010;
    int fa[MAXN];
    int val[MAXN];
    
    int Find(int x)
    {
        if (x == fa[x]) return x;
        int tmp = fa[x];
        fa[x] = Find(fa[x]);
        val[x] = val[x] ^ val[tmp];
        return fa[x];
    }
    
    int main()
    {
       // freopen("sample.txt","r",stdin);
        int T,N,M,kase = 1;
        bool first = true;
        scanf("%d",&T);
        while (T--)
        {
            if (first) first = false;
            else putchar('
    ');
            scanf("%d%d",&N,&M);
            for (int i = 0 ; i <= N ; i++)
            {
                fa[i] = i;
                val[i] = 0;
            }
            bool flag = false;
            for (int i = 0 ; i < M ; i++)
            {
                int u ,v;
                scanf("%d%d",&u,&v);
                if (flag) continue;
                int fu = Find(u);
                int fv = Find(v);
                if (fu == fv)
                {
                    int res = val[u] ^ val[v];
                    if (res == 0)
                    {
                        flag = true;
                    }
                }
                else
                {
                    fa[fv] = fu;
                    val[fv] = val[u] ^ val[v] ^ 1;
                }
            }
            printf("Scenario #%d:
    ",kase++);
            if (flag) puts("Suspicious bugs found!");
            else puts("No suspicious bugs found!");
        }
        return 0;
    }
    View Code
    
    

     POJ 2912 Rochambeau

    难点是要注意到N很小只有500,那么可以枚举裁判是谁。然后用类似食物链的方法并查集维护处假话。

    分情况输出,如果只有一个裁判的情况下。那么分辨出的步数就是不是确定裁判的所有枚举中的最大矛盾round

    怎么维护并查集看前面。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 510;
    int fa[MAXN];
    int val[MAXN];
    
    int Find(int x)
    {
        if (x == fa[x]) return x;
        int tmp = fa[x];
        fa[x] = Find(fa[x]);
        val[x] = (val[x] + val[tmp]) % 3;
        return fa[x];
    }
    
    int N,M;
    struct node
    {
        int u,v;
        int op;
    }src[4010];
    char str[50];
    
    int main()
    {
        while (scanf("%d%d",&N,&M) != EOF)
        {
            gets(str);
            for (int i = 0 ; i < M ; i++)
            {
                gets(str);
                int pos;
                int len = strlen(str);
                for (pos = 0 ; pos < len ; pos++) if (str[pos] == '=' || str[pos] == '>' || str[pos] == '<') break;
                int u = 0;
                for (int j = 0 ; j < pos ; j++)
                {
                    u = u * 10 + str[j] - '0';
                }
                int v = 0;
                for (int j = pos + 1 ; j < len ; j++)
                {
                    v = v * 10 + str[j] - '0';
                }
                src[i].u = u;
                src[i].v = v;
                if (str[pos] == '=') src[i].op = 0;
                else if (str[pos] == '<') src[i].op = 1;
                else src[i].op = 2;
            }
           // for (int i = 0 ; i < M ; i++)
              //  printf("%d %d %d
    ",src[i].u,src[i].v,src[i].op);
            int ansi,anst = 0;
            int cnt = 0;
            for (int i = 0 ; i < N ; i++)
            {
                for (int j = 0 ; j < N ; j++) fa[j] = j;
                memset(val,0,sizeof(val));
                int pos = -1;
                for (int j = 0 ; j < M ; j++)
                {
                    if (src[j].u == i || src[j].v == i) continue;
                    int u = src[j].u;
                    int v = src[j].v;
                    int fu = Find(u);
                    int fv = Find(v);
                    if (fu == fv)
                    {
                        if ((3 - src[j].op + val[u]) % 3 != val[v])
                        {
                            pos = j + 1;
                            break;
                        }
                    }
                    else
                    {
                        fa[fv] = fu;
                        val[fv] = (6 - val[v] - src[j].op + val[u]) % 3;
                    }
                }
                if (pos == -1)
                {
                    cnt++;
                    ansi = i;
                }
                else anst = max(anst,pos);
            }
            if(cnt == 0)printf("Impossible
    ");
            else if(cnt >= 2)printf("Can not determine
    ");
            else
               printf("Player %d can be determined to be the judge after %d lines
    ",ansi,anst);
    
        }
        return 0;
    }
    View Code

    ZOJ 3261 Connections in Galaxy War

    正向在线做的话有删除操作很难处理,就离线变成从后向前处理变为添加操作

    就是普通并查集

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 10010;
    const int MAXD = 50010;
    int fa[MAXN];
    int Find(int x) {return x == fa[x] ? x : fa[x] = Find(fa[x]);}
    int val[MAXN],pos[MAXN];
    void unionset(int u,int v)
    {
        int fu = Find(u),fv = Find(v);
        if (fu != fv)
        {
            fa[fu] = fv;
            if (val[fu] > val[fv])
            {
                val[fv] = val[fu];
                pos[fv] = pos[fu];
            }
            else if (val[fu] == val[fv] && pos[fu] < pos[fv])
            {
                pos[fv] = pos[fu];
            }
        }
    }
    int from[20010],to[20010];
    map<int,int>mp[MAXN];
    bool vis[20010];
    int p[MAXN];
    struct node
    {
        int op;
        int u,v;
    }ask[MAXD];
    int ret[MAXD];
    
    int main()
    {
        int N;
        bool first = true;
        while (scanf("%d",&N) != EOF)
        {
            if (first) first = false;
            else puts("");
            for (int i = 0 ; i < N ; i++)
            {
                scanf("%d",&p[i]);
                val[i] = p[i];
                pos[i] = i;
                mp[i].clear();
            }
            for (int i = 0 ; i <= N ; i++) fa[i] = i;
            int M;
            scanf("%d",&M);
            for (int i = 1 ; i <= M ; i++)
            {
                scanf("%d%d",&from[i],&to[i]);
                if (from[i] > to[i]) swap(from[i],to[i]);
                mp[from[i]][to[i]] = i;
                vis[i] = false;
            }
            int Q;
            scanf("%d",&Q);
            for (int i = 0 ; i < Q ; i++)
            {
                char op[20];
                scanf("%s",op);
                if(op[0] == 'q')
                {
                    ask[i].op = 0;
                    scanf("%d",&ask[i].u);
                }
                else
                {
                    ask[i].op = 1;
                    scanf("%d%d",&ask[i].u,&ask[i].v);
                    if (ask[i].u > ask[i].v) swap(ask[i].u,ask[i].v);
                    int tmp = mp[ask[i].u][ask[i].v];
                    vis[tmp] = true;
                }
            }
            for (int i = 1 ; i <= M ; i++)
            {
                if(!vis[i]) unionset(from[i],to[i]);
            }
            int tot = 0;
            for (int i = Q - 1 ; i >= 0 ; i--)
            {
                if (ask[i].op == 0)
                {
                    int fu = Find(ask[i].u);
                    if (val[fu] > p[ask[i].u]) ret[tot++] = pos[fu];
                    else ret[tot++] = -1;
                }
                else
                {
                    unionset(ask[i].u,ask[i].v);
                }
            }
            tot--;
            for (int i = tot ; i >= 0 ; i--) printf("%d
    ",ret[i]);
        }
        return 0;
    }
    View Code

    HDU 1272 小希的迷宫
    一个无向图是不是树。注意坑点 第一:森林,第二:空树

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 100010;
    int fa[MAXN];
    int res[MAXN * 10];
    int tot;
    int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
    int main()
    {
       // freopen("sample.txt","r",stdin);
        int u,v;
        while (scanf("%d%d",&u,&v) != EOF)
        {
            if (u == -1 && v == -1) break;
            if (u == 0 && v == 0)
            {
                puts("Yes");
                continue;
            }
            for (int i = 0 ; i < MAXN ; i++) fa[i] = i;
            bool flag = false;
            int fu = Find(u);
            int fv = Find(v);
            if (fu != fv) fa[fv] = fu;
            tot = 0;
            res[tot++] = u;
            res[tot++] = v;
            while (scanf("%d%d",&u,&v) != EOF)
            {
                if (u == 0 && v == 0) break;
                if (flag) continue;
                int fu = Find(u);
                int fv = Find(v);
                res[tot++] = u;
                res[tot++] = v;
                if (fu == fv) flag = true;
                else
                {
                    fa[fv] = fu;
                }
            }
            sort(res,res + tot);
            tot = unique(res,res + tot) - res;
           // for (int i = 0 ; i < tot ; i++) printf("%d ",res[i]); puts("");
            int cnt = 0;
            for (int i = 0 ; i < tot ; i++)
            {
                if (Find(res[i]) == res[i])
                    cnt++;
                if (cnt >= 2)
                {
                    flag = true;
                    break;
                }
            }
            if (flag) puts("No");
            else puts("Yes");
        }
        return 0;
    }
    View Code

    POJ 1308 Is It A Tree?

    有向图是不是树

    连通后仅有一个点的入度为0,其他所有点的入度都为1 即为树

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <climits>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define LL long long
    #define PI 3.1415926535897932626
    using namespace std;
    int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
    const int MAXN = 1000010;
    int fa[MAXN];
    int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
    int deg[MAXN];
    bool vis[MAXN];
    
    //这题用并查集判断连通,连通后有且仅有1个入度为0,其余入度为1,就是树了。
    
    //注意树为空的情况。
    
     
    int main()
    {
        int u,v,kase = 1;
        while (scanf("%d%d",&u,&v) != EOF)
        {
            if (u == -1 && v == -1) break;
            if (u == 0 && v == 0)
            {
                printf("Case %d is a tree.
    ",kase++);
                continue;
            }
            memset(vis,false,sizeof(vis));
            vis[u] = vis[v] = true;
            memset(deg,0,sizeof(deg));
            deg[v]++;
            for (int i = 0 ; i < MAXN ; i++) fa[i] = i;
            fa[v] = u;
            while (scanf("%d%d",&u,&v) != EOF)
            {
                if (u == 0 && v == 0) break;
                int fu = Find(u);
                int fv = Find(v);
                if (fu != fv)
                {
                    fa[fv] = fu;
                }
                vis[u] = vis[v] = true;
                deg[v]++;
            }
            int num = 0,cnt0 = 0,cnt1 = 0;
            int pos;
            bool flag = false;
            for (int i = 1 ; i < MAXN ; i++)
            {
                if (vis[i])
                {
                    pos = i;
                    break;
                }
            }
            int need = Find(pos);
            for (int i = pos ; i < MAXN ; i++)
            {
                if (vis[i])
                {
                    num++;
                    if (deg[i] == 0) cnt0++;
                    else if (deg[i] == 1) cnt1++;
                    if (Find(i) != need)
                    {
                        flag = true;
                        break;
                    }
                }
            }
            if (cnt0 == 1 && cnt1 + cnt0 == num && !flag) printf("Case %d is a tree.
    ",kase++);
            else printf("Case %d is not a tree.
    ",kase++);
        }
        return 0;
    }
    View Code
    
    
    
    
    
    
     
     
    
    
    
     
  • 相关阅读:
    (转)Tomcat7+Redis存储Session
    (转)Nginx SSL+tomcat集群,request.getScheme() 取到https正确的协议
    Oracle行转列、列转行的Sql语句总结(转)
    http升https笔记
    quartz 线程问题
    SQL语句汇总
    IDEA 入坑
    ssm 配置文件intit
    黑马-springMvC 运行加载顺序
    实训--git 好文
  • 原文地址:https://www.cnblogs.com/Commence/p/4902623.html
Copyright © 2011-2022 走看看