1 条题解

  • 0
    @ 2025-8-24 22:38:45

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar LYqwq
    RP+=+∞ || 不拿钩 7 不改签名

    搬运于2025-08-24 22:38:45,当前版本为作者最后更新于2022-06-12 21:08:39,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    update on 2022/6/20 发现题目链接错了,修复。\textsf{update on 2022/6/20 发现题目链接错了,修复。}


    题目传送门

    题目大意

    一个班级被分成 gg 个组,每组三个人,现有一些约束:

    • 某对学生必须在同一小组。
    • 某对学生必须不在同一小组。

    现给出分组方案,以及约束条件若干条,求有多少条约束不成立。

    思路

    双倍经验

    既然涉及到分组,那么我的第一反应就是并查集。

    使用并查集记录学生的分组情况,然后对所有约束条件一一判断,若有不成立的条件,就计入答案。

    但还有一个问题:学生的名字为字符串,不是数字。

    于是就需要将这些字符串转化为数字。

    考虑用 map 记录所有名字的下标,如果第一次遇到某个名字 ssm[s] 一定为 00,这种情况给 ss 分配下一个未使用的下标即可。注意下标不能使用 00,否则每次都会重新分配下标。

    但这种方法直接交上去会 TLE,吸氧才能 AC

    作为一个非不吸氧 A 题不可的人,我决定再考虑一下字符串哈希。

    可是很多种 hash 方法都会 WA 几个点。

    于是将并查集范围开到 10710^7,试图减少 hash 冲突...

    还是一片红......

    最终得到,使用 BKDR hash 可过此题。

    上面那篇文章中说到,need2n12^n-1 比较好,那么我们就取 2612^6-1 吧,也就是 6363

    代码是这样的:

    int hs(string s){
        unsigned int res=0;
        for(int i=0; i<s.size(); i++)
            res=(res*63)^s[i];
        return res%(int)1e7; // 最后需要mod1e7,
    }
    

    再交一发,这题就没了

    代码

    #include <iostream>
    using namespace std;
    const int N=1e5+5;
    int x,y,g,ans;
    string n1[N][2],n2[N][2],a,b,c;
    
    template<class T=int>
    class set{
        public:
            set(int n=1e5){f=new T[l=n+5]; clear();}
            ~set(){delete[] f;}
            T find(T x){return f[x]==x ? x : f[x]=find(f[x]);}
            void merge(T x,T y){f[find(x)]=find(y);}
            void clear(){for(int i=0; i<l; i++) f[i]=i;}
        private:
            T *f;
            int l;
    };
    set<int> s(1e7); // 并查集(构造函数传范围)
    
    // 字符串哈希
    int hs(string s){
        unsigned int res=0;
        for(int i=0; i<s.size(); i++)
            res=res*63+s[i]; // BKDR 哈希
        return res%(int)1e7;
        // res需要作为数组下标,为了防止哈希冲突,将数组开到了 1e7
    }
    
    int main(){
        scanf("%d",&x); // 约束 1 个数
        for(int i=1; i<=x; i++) cin >> n1[i][0] >> n1[i][1];
        scanf("%d",&y); // 约束 2 个数
        for(int i=1; i<=y; i++) cin >> n2[i][0] >> n2[i][1];
        scanf("%d",&g); // 组数
        for(int i=1; i<=g; i++){
            cin >> a >> b >> c; // a,b,c 在同一组
            s.merge(hs(a),hs(b)); // 将其合并
            s.merge(hs(a),hs(c));
        }
        for(int i=1; i<=x; i++) // 判断是否违反约束 1
            if(s.find(hs(n1[i][0]))!=s.find(hs(n1[i][1]))) ans++;
        for(int i=1; i<=y; i++) // 同上,约束 2
            if(s.find(hs(n2[i][0]))==s.find(hs(n2[i][1]))) ans++;
        printf("%d\n",ans);
        return 0;
    }
    
    • 1

    信息

    ID
    7740
    时间
    1000ms
    内存
    128MiB
    难度
    2
    标签
    递交数
    0
    已通过
    0
    上传者