牛客網練習賽15__吉姆的奇思妙想
阿新 • • 發佈:2019-01-11
題目描述
吉姆是個熱愛演算法競賽的小朋友,平常的休閒活動就是刷 牛客網 的題目。當吉姆刷到 wannafly挑戰賽12 F.小H和聖誕樹 這題時,頗為震驚,因為這是他第一次在wannafly挑戰賽上看到作者提供的解答的時間複雜度的式子裡含有根號的題目,於是吉姆就開始在網路上搜尋擁有類似時間複雜度解法的問題,並且看到了以下這題:
給你一個有 N 個點 M 條邊的無向簡單圖,請算算此圖中有幾個三角形。我們稱無序的三個點 x,y,z 為三角形,若且唯若 (x,y)、(y,z)、(x,z) 都是此圖上的邊。吉姆 想了這個問題七天七夜後,他發現了一件事情:
(1) 對於任一個度數為 d 的點,我們可以用 O(d2
吉姆 又想了這個問題七天七夜後,他又發現了一件事情:
(2) 對於任一個點,我們可以用 O(M) 的時間複雜度來計算有多少三角形包含該點。
吉姆又再想了這個問題十四天十四夜後,他忽然想到:
(3) 不如就把 (1) 和 (2) 的演算法結合在一起!找一個恰當的整數s 若一個點度數小於等於s,就使用(1),否則就使用(2),經過縝密的時間複雜度分析,發現這麼做的時間複雜度會是,取 ,時間複雜度就會是。最後再把包含各個點的三角形數全部加起來再除以 3 就是答案了! (因為每個三角形會被算到三次。) (此時間複雜度的詳細證明會在今天的題解中解說唷~)
但這個 s 的值究竟要設為多少呢?這就和 (1) 與 (2) 兩種方法的執行時間的常係數有關了。
於是,吉姆為了透徹瞭解這個問題,就假定(1) 和(2) 的常係數分別是a 與b,這意思是:對於一個度數為d 的點,若使用(1),程式的執行時間和a×d2
對於一個給定的圖,吉姆想要知道對於不同的 a,b,s 的值要取多少比較好,並且求出 s 為該值時程式所需的執行時間!
在這個問題裡,我們不會真的給你一個圖,只會告訴你邊數 M。並對於所有正整數 d,告訴你度數為 d 的點有幾個。甚至,題目裡給你的圖不一定實際上存在的簡單圖(意即輸入可能不是一個合法的簡單圖的度序列)。大家好,以上是題目背景。(哈哈哈哈哈嚯嚯嚯嚯~)給你兩個正整數 M, L, 以及兩個長度為 L 個正整數序列 deg1,deg2,..., degL 和 freq1, freq2,..., freqL。
(degi 和 freqi
你要回答 Q 個問題,第 i 個問題會給你 2 個正整數 ai, bi,請找到一個整數 s 使得以下式子(Ei) 的值最小:
(此式就是 Background 裡提到的程式執行時間的估計函式)
輸入描述:
輸入共有 1+L+1+Q 行。 第一行有有兩個正整數 M,L。 接下來的 L 行中的第 i 行有兩個正整數 degi 和 freqi。 下一行有一個正整數 Q。 最後 Q 行中的第 i 行有兩個正整數 ai, bi。
輸出描述:
對於每個詢問都輸出一行包含一個整數,代表式子 Ei 的最小值。
示例1輸入
5 4 1 1 2 1 3 1 4 1 5 1 1 3 2 2000 1 1 2000 2000 2000
輸出
15 33 20 30 30000
說明
取 s=2 將會有 E1 的最小值:12×1+22×1+5×1+5×1=15。
備註:
1≤M≤1010,1≤L≤2×105,1≤degi,freqi≤1010,1≤Q≤5×104,1≤ai,bi≤2×103,對於所有 1≤i<L,都有 degi<degi+1對於所有可能測試資料的答案都能使用 64 bit 有符號整數表示。
思路:可以用三分搜尋解;
能用三分搜求極小值的充分條件是:該函式在我們會詢問的定義域下,是先嚴格遞減再嚴格遞增。
於是我們來觀察看看s = deg[ j ]和 s = deg[ j + 1 ]時,E[ i ]是如何變化的。
結果發現,它的變化量Δj是freg[ j ]*(a[ i ] * deg[ j ]*deg[ j ] - b[ i ]*M),而deg[ j ]是隨著j增加而嚴格遞增的序列,故Δj確實是先嚴格遞減再嚴格遞增。
程式碼:
#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const int maxn = 2*1e5+7;
ll M;
int L, Q;
ll deg[maxn], freq[maxn];
ll dp_front[maxn], dp_back[maxn];
int main()
{
memset(dp_front, 0, sizeof(dp_front));
memset(dp_back, 0, sizeof(dp_back));
scanf("%lld%d", &M, &L);
for(int i = 1; i <= L; i++) scanf("%lld%lld", °[i], &freq[i]);
for(int i = 1; i <= L; i++) dp_front[i] = dp_front[i - 1] + deg[i] * deg[i] * freq[i];
for(int i = L; i > 0; i--) dp_back[i] = dp_back[i + 1] + M * freq[i];
scanf("%d", &Q);
while(Q--) {
int a, b;
scanf("%d%d", &a, &b);
int it = upper_bound(deg + 1, deg + L + 1, (ll)sqrt(b*M/a)) - deg;
printf("%lld\n", a*dp_front[it - 1] + b*dp_back[it]);
}
return 0;
}