1. 程式人生 > >11.06T2 置換群

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