11.06T2 置换群
1.聪明的农民3362
(clever.cpp/c/pas)
【问题描述】
在城中有一个贪婪而愚蠢的领主,他总是不管农民的死活,疯狂地敲诈农民们辛勤耕种的劳动成果。眼看每年的秋收日十月十日已经到了,这时城中所有的农民在忙完一年的收成后,又要向领主上缴一年的粮食了。很久以来当地就采用一个特殊的“抽签”决定各户农民上缴粮食的方法:领主将一些写有不同的数的纸条放入一个盒子中,每户农民从中抽取两张纸条并在第二天上缴和两张纸条上的数值相乘结果同样数量的粮食。
城中的农民是个既聪明又团结的整体,他们非常痛恨残暴的领主,不希望向他多交一粒粮食,所以他们每年在抽完签后,秘密地交换一些纸条,使最后向领主上缴的粮食总量最少。不幸的是,以往决定如何交换纸条的老农夫杰克病倒了,不能为大家出力了,所以请你帮助他们决定如何交换纸条。另外,为了瞒过领主,活动只能在夜间悄悄进行,因此务必要使交换次数最少(两个农民将各自的一张纸条对调叫做一次交换)。
【输入】
文件的第一行为农户数目n(n≤500),接下来的n行,每行为两个数a、b,其中a、b两数均不超过1000。相邻两个数之间由一个或多个空格分隔。
【输出】
输出文件由两个数组成,每个数占一行,分别为交换后向领主上缴的粮食总量和需要交换的总次数。
【输入样例】
3
10 13
7 12
9 6
【输出样例】
252
1
【分析】贪心+置换
排序不等式+带权并查集
code:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define N 1000005 5 using namespace std; 6 struct node{ 7 long long a,b; 8 }e[N]; 9 long long c[N],d[N],fa[N],siz[N],cnt,from[N]; 10 long long find(long long x){ 11 if(x!=fa[x])return fa[x]=find(fa[x]); 12 return fa[x]; 13 } 14 void merge(long long x,long long y){ 15 long long f1=find(x),f2=find(y); 16 if(f1!=f2){ 17 fa[f1]=f2; 18 siz[f2]+=siz[f1]; 19 } 20 } 21 long long vis[N]; 22 int main(){ 23 long long n; 24 cin>>n; 25 for(long long i=1;i<=n;i++){ 26 cin>>e[i].a>>e[i].b; 27 d[++cnt]=e[i].a; 28 d[++cnt]=e[i].b; 29 } 30 sort(d+1,d+cnt+1); 31 long long Ans=0; 32 for(long long i=0;i<n;i++){ 33 Ans+=d[i+1]*d[2*n-i]; 34 } 35 for(long long i=1;i<=2*n;i++)fa[i]=i,siz[i]=1; 36 cout<<Ans<<'\n'; 37 cnt=unique(d+1,d+cnt+1)-d-1; 38 for(long long i=1;i<=n;i++){ 39 e[i].a=lower_bound(d+1,d+cnt+1,e[i].a)-d; 40 e[i].b=lower_bound(d+1,d+cnt+1,e[i].b)-d; 41 from[e[i].b]=i; 42 from[e[i].a]=i; 43 } 44 for(long long i=1;i<=n;i++){ 45 merge(i,from[2*n-e[i].a+1]); 46 merge(i,from[2*n-e[i].b+1]); 47 } 48 Ans=0; 49 for(long long i=1;i<=n;i++){ 50 if(vis[find(i)])continue; 51 Ans+=siz[find(i)]-1; 52 vis[find(i)]=1; 53 } 54 cout<<Ans; 55 return 0; 56 }
over