生活随笔
收集整理的這篇文章主要介紹了
HDU - 6982 J - Road Discount wqs二分 + 模型转换 + 优化
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
傳送門
文章目錄
題意:
給你一個(gè)nnn個(gè)點(diǎn)mmm條邊的圖,每個(gè)邊有一個(gè)代價(jià)以及折扣價(jià),你需要輸出nnn行,第iii行代表你可以選i?1i-1i?1條邊使其變成優(yōu)惠價(jià),問(wèn)每次的最小生成樹的代價(jià)是多少。
n≤1e3,m≤2e5,ci,di≤1e3n\le 1e3,m\le2e5,c_i,d_i\le 1e3n≤1e3,m≤2e5,ci?,di?≤1e3
思路:
直接考慮折扣價(jià)不是很好想,所以考慮能不能把這些邊單獨(dú)拿出來(lái)。
下面我們假定原邊是白邊,折扣邊是黑邊,那么對(duì)于每次要輸出的,問(wèn)題就轉(zhuǎn)換成了選kkk條黑邊的最小生成樹的代價(jià)是多少。
顯然這是一個(gè)wqswqswqs二分的一個(gè)經(jīng)典問(wèn)題,我們?cè)O(shè)這個(gè)函數(shù)是f(k)f(k)f(k),這是一個(gè)凸函數(shù),我們二分一個(gè)值midmidmid,之后將所有黑邊的權(quán)值都加上midmidmid,讓后跑最小生成樹,假設(shè)選擇了cntcntcnt條黑邊,且總代價(jià)是sumsumsum,那么如果cnt>=kcnt>=kcnt>=k的話,顯然可以更新ans=sum?k?midans=sum-k*midans=sum?k?mid ,讓后調(diào)整一下左右邊界即可。
直接跑的話復(fù)雜度是O(nmlognlogn)O(nmlognlogn)O(nmlognlogn)的,雖然第二個(gè)logloglog是最小生成樹的,常數(shù)很小,但仍是過(guò)不了,考慮優(yōu)化。
考慮每次都有很多無(wú)用邊,即非樹邊是無(wú)用的,所以直接去掉非樹邊即可,將邊縮小到O(n)O(n)O(n)級(jí)別的,但是n2lognlognn^2lognlognn2lognlogn想過(guò)這個(gè)有101010個(gè)測(cè)試點(diǎn)的題還是不可能的。
繼續(xù)優(yōu)化,考慮對(duì)于每次二分,他的信息是可以復(fù)用的,且邊權(quán)在[1,1000][1,1000][1,1000]范圍內(nèi),所以可以預(yù)處理出來(lái),之后每次查詢二分的話直接使用已有信息即可。
復(fù)雜度O(n2a+nlogn)O(n^2a+nlogn)O(n2a+nlogn),其中n2an^2an2a的aaa是并查集的常數(shù),很小。
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std
;
typedef long long LL
;
typedef unsigned long long ULL
;
typedef pair
<int,int> PII
;const int N
=1000010,mod
=1e9+7,INF
=0x3f3f3f3f;
const double eps
=1e-6;int n
,m
;
int p
[N
],ans
,cnt
;
PII f
[N
];
struct Node {int x
,y
,w
,add
,op
;bool operator < (const Node
&W
) const {return w
<W
.w
;}
}edge1
[N
],edge2
[N
],edge
[N
];int find(int x
) {return x
==p
[x
]? x
:p
[x
]=find(p
[x
]);
}PII
check(int mid
) {for(int i
=1;i
<=m
;i
++) edge2
[i
].w
+=mid
;int tot
=0;edge1
[m
+1].w
=INF
; edge2
[m
+1].w
=INF
;for(int i
=1,j
=1;i
<=m
||j
<=m
;) {if(edge1
[i
].w
<edge2
[j
].w
) edge
[++tot
]=edge1
[i
++];else edge
[++tot
]=edge2
[j
++];}cnt
=0; ans
=0;for(int i
=1;i
<=n
;i
++) p
[i
]=i
;for(int i
=1;i
<=tot
;i
++) {int a
=edge
[i
].x
,b
=edge
[i
].y
,w
=edge
[i
].w
,op
=edge
[i
].op
;int pa
=find(a
),pb
=find(b
);if(pa
==pb
) continue;p
[pa
]=pb
; cnt
+=op
==0;ans
+=w
;}for(int i
=1;i
<=m
;i
++) edge2
[i
].w
-=mid
;return {cnt
,ans
};
}int main()
{
int _
; scanf("%d",&_
);while(_
--) {scanf("%d%d",&n
,&m
);for(int i
=1;i
<=m
;i
++) scanf("%d%d%d%d",&edge1
[i
].x
,&edge1
[i
].y
,&edge1
[i
].w
,&edge1
[i
].add
),edge1
[i
].op
=1;for(int i
=1;i
<=m
;i
++) {edge2
[i
]=edge1
[i
],edge2
[i
].w
=edge1
[i
].add
;edge2
[i
].op
=0;}sort(edge1
+1,edge1
+1+m
); sort(edge2
+1,edge2
+1+m
);int tot
=0;for(int i
=1;i
<=n
;i
++) p
[i
]=i
;for(int i
=1;i
<=m
;i
++) {int a
=edge1
[i
].x
,b
=edge1
[i
].y
,w
=edge1
[i
].w
,op
=edge1
[i
].op
;int pa
=find(a
),pb
=find(b
);if(pa
==pb
) continue;p
[pa
]=pb
; edge1
[++tot
]=edge1
[i
];}tot
=0;for(int i
=1;i
<=n
;i
++) p
[i
]=i
;for(int i
=1;i
<=m
;i
++) {int a
=edge2
[i
].x
,b
=edge2
[i
].y
,w
=edge2
[i
].w
,op
=edge2
[i
].op
;int pa
=find(a
),pb
=find(b
);if(pa
==pb
) continue;p
[pa
]=pb
; edge2
[++tot
]=edge2
[i
];}m
=n
-1;for(int i
=-1010;i
<=1010;i
++) f
[i
+1010]=check(i
);for(int k
=0;k
<n
;k
++) {int l
=-1010,r
=1010,res
;while(l
<=r
) {int mid
=(l
+r
)/2;if(f
[mid
+1010].X
>=k
) res
=f
[mid
+1010].Y
-mid
*k
,l
=mid
+1;else r
=mid
-1;}printf("%d\n",res
);}}return 0;
}
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)
總結(jié)
以上是生活随笔為你收集整理的HDU - 6982 J - Road Discount wqs二分 + 模型转换 + 优化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。