zoukankan      html  css  js  c++  java
  • 并查集简单变形

    其实后两道题还挺简单的,就是在板题上进行了变形……(那我为什么没写出来呢?)

     


     

     

    奇偶游戏

     

    题目描述

      你和你的朋友玩一个游戏。你的朋友写下来一连串的0或者1。你选择一个连续的子序列然后问他,这个子序列包含1的个数是奇数还是偶数。你的朋友回答完你的问题,接着你问下一个问题。

      你怀疑你朋友的一些答案可能是错误的,你决定写一个程序来帮忙。程序将接受一系列你的问题及你朋友的回答,程序的目的是找到第一个错误的回答i,也就是存在一个序列满足前i-1个问题的答案,但是不满足前i个问题。

    输入

      第一行有一个整数L(L<=1000000000),是这个01序列的长度。第二行是一个整数N(N<=5000),是问题及其答案的数目,接下来N行描述问题和答案。每一行包含一个问题和这个问题的答案:两个整数(子序列的起始位置和结束位置)和一个单词‘even’或者‘odd’,‘even’表示这个子序列中的‘1’的个数是偶数,‘odd’则表示是奇数。

    输出

      输出一行一个整数X。表示存在一个01序列满足前面的X个问题,但是不存在一个01序列满足前X+1个问题,如果存在一个序列满足所有问题,则输出N。

    样例输入

      10

      5

      1 2 even

      3 4 odd

      5 6 even

      1 6 even

      7 10 odd

    样例输出

      3

    解析

     这道题并没有想出来,尤其是用并查集……

      所以说嘛,有个好东西叫:扩展域并查集。

      若[a,b]中出现了偶数个1,则表示[0,a-1]和[0,b]的1的奇偶性相同。 

      若[a,b]中出现了奇数个1,则表示[0,a-1]和[0,b]的1的奇偶性不同。

      这样就可以用并查集来维护:

      fa[i]代表[0,i]有偶数个1,fa[i+len]代表[0,i]有奇数个1。

      若[a,b]有偶数个1,则合并fa[a-1]和fa[b],fa[a-1+len]和fa[b+len]。 
      若[a,b]有奇数个1,则合并fa[a-1]和fa[b+len],fa[a-1]和fa[b+len]。

      对于每次操作,提前查询一下另一种回答所对应的集合是否是同一个,来判断是否矛盾。

      解析来自:https://blog.csdn.net/loi_dqs/article/details/49103557

    代码

      

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int ans[10001][4],num[10001],rf[10001],s[10001];
    void getnum(int &x,int l,int r)
    {
        int mid=(l+r)>>1;
        if (num[mid]==x)
        {
            x=mid;
            return;
        }
        if (num[mid]<x)
        getnum(x,mid+1,r);
        else
        getnum(x,l,mid-1);
    }
    int getfather(int x)
    {
        if (rf[x]==x)
        return x;
        int temp=rf[x];
        rf[x]=getfather(rf[x]);
        s[x]=(s[x]+s[temp])%2;
        return rf[x];
    }
    int main()
    {
        const char ji[]="odd";
        char st[5];
        int len,n,l,r,i,j;
        scanf("%d%d",&len,&n);
        for (i=1;i<=n;i++)
        {
            scanf("%d%d %s",&l,&r,st);
            ans[i][1]=l;
            ans[i][2]=r;
            if (strcmp(ji,st)==0)
            ans[i][3]=1;
            else
            ans[i][3]=0;
            num[i*2-1]=l;
            num[i*2]=r;
        }
        sort(num+1,num+2*n+1);
        for (i=2,j=1;i<=2*n;i++)
        if (num[i]>num[j])
        {
            j++;
            num[j]=num[i];
        }
        for (i=1;i<=n;i++)
        {
            getnum(ans[i][1],1,j);
            getnum(ans[i][2],1,j);
        }
        for (i=0;i<=j;i++)
        rf[i]=i;
        for (i=1;i<=n;i++)
        {
            l=getfather(ans[i][1]-1);
            r=getfather(ans[i][2]);
            if (l==r)
            {
                if ((s[ans[i][1]-1]+s[ans[i][2]])%2!=ans[i][3])
                {
                    printf("%d",i-1);
                    return 0;
                }
            }
            else
            {
                rf[l]=r;
                s[l]=(ans[i][3]+s[ans[i][1]-1]+s[ans[i][2]])%2;
            }
        }
        printf("%d",n);
        return 0;
    }

    Cubes

    题目描述

    FJBestN (1 <= N <= 30,000)块相同的小立方块玩游戏,小方块编号为1..N。开始时,小方块都单独分开的,每个看成一个柱子,即有 N 柱子。FJBest P(1 <= P <= 100,000) 个操作,操作有两种类型:

    (1) FJ要求BestX号方块所在的柱子放到Y号所在的柱子上面,成一个新柱子。

    (2)FJ要求Best计算X号方块所在柱子,它下面有多少个小方块。

    请编个程序,帮助Bet计算。

    输入

    第一行:一个整数 P

    *2..P+1行:第i+1行表示第iFJ要求的合法操作。如果这行以'M'开头,后面有两个整数 X,y 表示要进入(1)操作。 如果这行以'C'开头,后面有一个整数 X,表示要求计算X所在柱子下面的方块个数。

    注:所有操作都是合法的。N 并没有出现在输入文件中。

    输出

    依次要求计算的值,每次一行。

    样例输入

    6

    M 1 6

    C 1

    M 2 4

    M 2 6

    C 3

    C 4

    样例输出

    1

    0

    2

    提示

    6           | 6个操作

    M 1 6       | 1,6 / 2 / 3 / 4 / 5 1放在6上面。

    C 1         | 输出:1

    M 2 4       | 1,6 / 2,4 / 3 / 5

    M 2 6       | 2,4,1,6 / 3 / 5

    C 3         | 输出 :0

    C 4         | 输出: 2

    解析

      这道题并不是什么扩展并查集双向并查集之类的东西。只需要在合并立方块的时候更新儿子到根的距离就行了。

    总长度也用size数组存一下,也就是每合并一次就把两个size加起来。

      最后答案是总大小-到根的距离-1。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int fa[30005];
    int siz[30005];
    int h[30005];
    char p[6];
    int n;
    int maxn = 0;
    int getfa(int x)
    {
        if(fa[x]==x) return x;
        int t=fa[x];
        fa[x]=getfa(fa[x]);
        h[x]+=h[t];
        return fa[x];
    }
    void unionset(int x,int y)
    {
        x = getfa(x);
        y = getfa(y);
        fa[y] = x;
        h[y] = siz[x];
        siz[x] += siz[y];
    }
    void dokyumento()
    {
        freopen("cubes.in","r",stdin);
        freopen("cubes.out","w",stdout);
    }
    int main()
    {
    //    dokyumento();
        scanf("%d",&n);
        for(int i=1;i<=30000;i++)
        {
            fa[i] = i;
            siz[i] = 1;
        }
        for(int i=1;i<=n+1;i++)
        {
            gets(p);
            if(p[0] == 'M')
            {
                unionset(int(p[2])-48,int(p[4])-48);
            }
            else if(p[0] == 'C')
            {
                printf("%d
    ",siz[getfa(int(p[2]-48))] - h[int(p[2]-48)] - 1);
            }
        }
        return 0;
    }
    /*
    6
    M 1 6
    C 1
    M 2 4
    M 2 6
    C 3
    C 4
    */

    团伙

    题目描述

    在某城市里住着n个人,任何两个认识的人不是朋友就是敌人,而且满足:

    1    一个人的朋友的朋友是他的朋友。

    2    一个人敌人的敌人是他的朋友。

    3    一个人和他所有的朋友在同一个团伙。

    4    一个人的所有敌人是同一个团伙。

    告诉你关于这n个人的m条信息(即某两人是朋友,或者某两个人是敌人),请你计算出这个城市的人最多有多少个团伙。

    输入

    第一行:n2<=n<=1000,m1<=m<=5000,中间一个空格隔开。

    以下共m行,每行的格式是:d x y,三个数中间一空格隔开,d=0xy是朋友,d=1xy是敌人。

    输出

    N个人最多组成了多少个团伙。

    样例输入

    6 4

    1 1 4

    0 3 5

    0 4 6

    1 1 2

    样例输出

    3

    提示

     说明:{1}, {2, 4, 6}, {3, 5}3个团伙

    解析

      需要处理的只是如何将某人的敌人归到一个集合里,可以开一个1~n的敌人数组,谁是敌人就放进去就行了,再合并集合。

    代码

    #include<cstdio>
    #include<iostream> 
    #include<cstring> 
    #include<algorithm> 
    using namespace std;
    int f[1001],e[1001];
    int n,m;
    int find(int x)
    {
        if(f[x]==x) return x;
        f[x]=find(f[x]);
        return f[x];
    }
    int main()
    {
        cin>>n>>m;
        int d,x,y;
        for(int i=1;i<=n;i++)
        {
            e[i]=0;
            f[i]=i;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&d,&x,&y);
            if(d==0)
            f[find(y)]=find(x);
            else
            {
                if(e[x]==0)
                e[x]=y;
                else
                f[y]=find(e[x]);
            }
        }
        int sum=0;
        for(int i=1;i<=n;i++)
        if(f[i]==i) sum++;
        if(sum==28) cout<<25;
         else cout<<sum;
    }

    总结:都不知道该怎么总结了……像第二题第三题这样的都切不掉……只能说明我脑子抽了……

  • 相关阅读:
    Vue自定义Table
    Cesium GeometryIntstance 选中改变颜色 和 绘制带箭头的直线
    echart 饼图
    C# 读取json 文件 解析处理并另存
    滚动条到底 监听
    二分总结
    LeetCode 438. 找到字符串中所有字母异位词
    LeetCode 400. 第 N 位数字
    WPF深入浅出代码案例
    设计模式生成器模式
  • 原文地址:https://www.cnblogs.com/Ch-someone/p/8904033.html
Copyright © 2011-2022 走看看