分组行动

Description

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

Input

  第一行4个正整数,NMQ
  第2~M+1行,每行3个正整数,a,b,c,表示编号为a的同学和编号为b的同学认识,并且要好程度是c。保证ab是不会相同的,也不会有多行描述相同两个学生之间的关系。 
  第M+2~M+Q+1行,每行2个正整数a,b,表示飞飞的一个问题,这里保证ab是不同的。 

Output

  一行一个整数,表示所有问题答案的乘积除以P的余数。

Sample Input

3 3 2 100

1 2 3

2 3 4

1 3 5

1 2

2 3

Sample Output

16

Hint

【样例解释】
  一共有3种不同的分组方法,{1}{2,3}{2}{1,3}{3}{1,2},代价分别是544。所以2个问题的答案都是4,所以最后结果就是4*4除以100的余数,是16 
【数据范围】
  对于20%的数据,N<16Q=5 
  对于30%的数据,N<1000Q=1 
  对于70%的数据,N<1000M<50000, C<10000P<10000 
  对于100%的数据,N<50000M<100000, Q<100000C<1000000P<1000000

题目大意:见说明。

因为是道比较综合的题目,因此也有多种解法。

解法1:整体二分,lx和ljr dalao水过~

解法2:Kruskal重构树(今年NOIday1t1) hch和hzy dalao水过~

解法3:最大生成树+路径最小边

简要解释一下,对于一个连通块,我们要断开其中的两个点,代价是其中较大的边,因此可以忽略小边,然后我们就可以得到一棵最大生成树。

然后就在树上骚搞。

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int N = 1e5+10;
int n,m,Q,Mod,vl[N];
int fa[N];
int siz[N],son[N],dep[N];
int cnt,top[N],p[N],fp[N];
struct Graph{
	vector<int>G[N];
	#define to G[x][i]
	inline void dfs2(int x,int sp){
		top[x]=sp;
		fp[p[x]=++cnt]=x;
		if(son[x])dfs2(son[x],sp);
		Inc(i,0,G[x].size()-1)if((to^fa[x])&&(to^son[x]))dfs2(to,to);
	}
	inline void dfs1(int x,int Fa,int depth){
		siz[x]=1;
		Inc(i,0,G[x].size()-1)if(to^Fa){
			dfs1(to,fa[to]=x,dep[to]=depth+1);
			siz[x]+=siz[to];
			if(siz[son[x]]<siz[to])son[x]=to;
		}
	}
}g;
struct SegMent{
	struct Tree{
		int L,r,mn;
	};
	Tree a[N<<2];
	#define Ls v<<1
	#define rs v<<1|1
	inline void pushup(int v){
		a[v].mn=min(a[Ls].mn,a[rs].mn);
	}
	inline int Query(int v,int A,int b){
		if(a[v].L>b||a[v].r<A)return 1<<30;
		if(A<=a[v].L&&a[v].r<=b)return a[v].mn;
		return min(Query(Ls,A,b),Query(rs,A,b));
	}
	inline void build(int v,int L,int r){
		int Mid=L+r>>1;
		a[v]=(Tree){L,r,1<<30};
		if(L==r)return a[v].mn=vl[fp[Mid]],void();
		build(Ls,L,Mid),build(rs,Mid+1,r);
		pushup(v);
	}
}tr;
struct MST{
	struct edge{
		int u,v,w;
		bool operator <(const edge&A) const{
			return w>A.w;
		}
	};
	edge a[N];
	int p[N];
	inline void init(){
		scanf("%d%d%d%d",&n,&m,&Q,&Mod);
		Inc(i,1,m){
			int x,y,z;scanf("%d%d%d",&x,&y,&z);
			a[i]=(edge){x,y,z};
		}
	}
	int getfa(int x){
		return x^p[x]?p[x]=getfa(p[x]):x;
	}
	inline void Kruskal(){
		sort(a+1,a+m+1);
		Inc(i,1,n)p[i]=i;
		memset(vl,63,sizeof(vl));
		Inc(i,1,m){
			int x=a[i].u,y=a[i].v;
			int fx=getfa(x),fy=getfa(y);
			if(fx==fy)continue;
			p[fx]=fy;
			vl[++n]=a[i].w;
			g.G[x].push_back(n),g.G[n].push_back(x);
			g.G[n].push_back(y),g.G[y].push_back(n);
		}
	}
}mst;
long long ans=1;
inline int ask(int x,int y){
	int tx=top[x],ty=top[y],mn=1<<30;
	while(tx^ty){
		if(dep[tx]<dep[ty])swap(tx,ty),swap(x,y);
		mn=min(mn,tr.Query(1,p[tx],p[x]));
		x=fa[tx],tx=top[x];
	}
	if(dep[x]>dep[y])swap(x,y);
	return min(mn,tr.Query(1,p[x],p[y]));
}
inline void solv(){
	tr.build(1,1,n);
	Inc(i,1,Q){
		int x,y;scanf("%d%d",&x,&y);
		(ans*=ask(x,y))%=Mod;
	}
	cout<<ans<<"\n";
}
int main(){
	mst.init();
	mst.Kruskal();
	g.dfs1(1,0,0);
	g.dfs2(1,1);
	solv();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/DancingZ/article/details/81484202