【生成树】分组行动

Description

 最近,木木中学要举行一年一度的辩论赛了,我们活泼开朗乐观向上不寂寞不生病不挂科天天回家吃饭的新时代好
少年--飞飞,自然是热情参与咯!辩论嘛,就有正方和反方两个组,这是一个传统项目,所以,包括飞飞,木木中
学的每一个学生都会加入2个组中的一个,不会有人打酱油的(如果有人打酱油,那么飞飞会义无反顾义不容辞的
上前用一翻惊天动地的演说打消他打酱油的念头的)。自然啦,作为有思想有文化能言善辩的好少年,飞飞,其实
加入哪个组,从技术角度分析真是无所谓的,不过呢,从另外一些角度来看,就复杂的多了……比如,飞飞是个很
不喜欢八卦的正义青年,所以啊,飞飞很不想和年级最PP的女生青菜分在一个组,因为会产生八卦的--为什么会有
八卦?首先是他们比较熟(比较熟就会有八卦吗?无限yy中……),最重要的就是因为飞飞是大帅哥啊!人长得帅
,有时候真是挺麻烦的,尤其飞飞还如此低调……再比如,飞飞也不想和小邱分在一个组--虽然他们不认识,但是
,飞飞听说小邱很懒还很暴力,飞飞不喜欢暴力……当然了,不论如何的分组行动,都会产生一些代价的,比如两
个好朋友分在了不同的组,那肯定不是很高兴了。飞飞知道,学校里有M对同学是相互认识的,每对认识的同学间
,都有一个要好程度C,自然的,关系越好,要好程度越高,也越不愿意分开。对于一个分组方案,必然会使得有
某些认识的同学分开,飞飞认为,一个分组方案的代价就是最不愿意分开的那两个同学之间的要好程度。飞飞在想
,和青菜MM分在不同的组最小代价是多少呢?和小邱同学分在不同的组最小代价是多少呢?如果……还要将青菜MM
和小邱分在不同的组最小代价是多少呢?……好麻烦啊,飞飞越想越乱,于是来找你帮忙了。一些说明:木木中学
有N个学生,木木中学是一所优秀的中学,学生们都是开朗外向的,所以一个学校里任意两个学生间接或者直接肯
定认识。显然任意一个辩论组都至少得有一个人。为了方便,我们把木木中学的学生用1到N编号,飞飞有Q个问题
,每个问题的形式都是这样的:将编号为a的学生与编号为b的学生分在不同组中,最少的代价是多少呢?关于这道
题目本身,只是想检验你能不能帮飞飞,所以你并不需要--具体回答每个问题,只需要最后输出每个问题答案的乘
积除以P的余数就可以了。

Input

输入文件中的第一行为四个正整数N,M,Q,P。
第二行至第M+1行,每行有三个正整数a,b,c,表示编号为a的同学和编号为b的同学认识,并且要好程度是c。
保证a和b是不会相同的,也不会有多行描述相同两个学生之间的关系。
第M+2行至第M+Q+1行,每行有两个正整数a,b,表示飞飞的一个问题,这里保证a和b是不同的。
N<5000,M<10000,Q<1000,C<1000000,P<1000000。

Output

输出文件中仅一行为一个整数,所有问题答案的乘积除以P的余数。

Sample Input

3 3 2 21
1 2 3
2 3 4
1 3 5
1 2
2 3

Sample Output

    16
【样例说明】
一共有3种不同的分组方法,{1}{2,3},{2}{1,3},{3}{1,2},代价分别是5,4和4。所以2个问题的答案都是4,所以最后结果就是4×4除以21的余数,是16。

分析

题目前面那一堆都是废话。真不知道出题人怎么想的。

这道题有点类似另外一道题链接

这道题大概是在说,我们要想出一种分组方法,让指定的某两个人分开,这样做的代价是让他们两个分开的最小边的最大值。

思路一:kruskal乱搞

首先我们把所有边降序排序,从最大的边开始插入。如果插入到某条边的时候这两个人被分到一组去了,那么这条边的权值就是分开这两个人的代价。

然后把所有这样的代价乘到一起就行了。

注意取模,不然容易出事

代码

#include<bits/stdc++.h>
using namespace std;
struct edge {
    int u, v, w;
} e[10010];
 
int n, m;
int q, p;
int fa[5010];
int a[1010], b[1010];
int qq[10000010], h, t = 1;
long long ans = 1;
 
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}
 
int find(int x) {
    if (x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}
 
bool cmp(edge a, edge b) {
    return a.w > b.w;
}
 
int main() {
//  freopen("group10.in", "r", stdin);
    n = read(), m = read(), q = read(), p = read();
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 1; i <= m; i++) e[i].u = read(), e[i].v = read(), e[i].w = read();
    for (int i = 1; i <= q; i++) {
        a[i] = read(), b[i] = read();
        qq[++h] = i;
    }
    sort(e + 1, e + m + 1, cmp);
    for (int i = 1; i <= m; i++) {
        int u = e[i].u, v = e[i].v, w = e[i].w;
        if (find(u) != find(v)) fa[find(u)] = find(v);
        int tmp = h;
        while (t <= tmp) {
//          cout << t << endl;
            int aa = a[qq[t]], bb = b[qq[t]];
            if (find(aa) == find(bb)) {
                ans = ans * w % p;
//              cout << qq[t] << " " << tmp << " " << t << " " << aa << " " << bb << " " << w << endl;
            }
            else {
                qq[++h] = qq[t];
            }//重新放回去 
            t++;
        }
    }
    printf("%lld", ans);
    return 0;
}

思路二:最大生成树上找最小边

首先我们跑一个最大生成树出来。

每次指定要被分开的两个人,我们在这棵最大生成树上,这两个人之间的路径中找到权值最小的那一条边,这条边的权值就是分开这两个人的代价

这样做明显要比上面哪一种方法快上许多。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值