IOI 2007 Sail 線段樹 貪心

2022-05-01 03:33:07 字數 3219 閱讀 3586

題意:

有一艘船,船上有 \(n\) 個旗桿,每個旗桿上有 \(h_i\) 個小節。每根旗桿上會掛 \(k_i\) 張帆

每個小節最多掛乙個帆。在風中,帆的不同排布方式會產生不同的推動力

對於任意一張帆,他的推動力折扣等於再它後面並且和它在同一高度的帆的數目

所有帆的任意一種位置組合的推動力折扣和等於在該位置下所有帆的推動力折扣的和

求所有位置組合最小的推動力折扣和

顯然有一種貪心方案:

設每個位置上的旗子數量為 \(s_i\),那麼按照旗桿長度從小到大排序

每次貪心的找到可行的位置中,旗子數量最少的 \(k\) 個放旗子即可(即 \(s_i+1\))

最後 \(\sum\limits_\frac\) 就是答案了。

時間複雜度 \(o(\sum\limits_^nh_i)\)

時間複雜度不行,如何優化?

按照 \(s_i\) 維護乙個排序後的數列,相當於每次給區間加 \(1\)

區間加 \(1\) 可能使得區間不再有序!

原來的 \(s_i:~[1,1,1,1,3]\)

\([1,3]\) 加 \(1\) 後的數列應為: \([1,2,2,2,3]\)

實際上也是區間加法!我們只要找到之後第乙個比之前這個數大的即可

可以利用線段樹優化,時間複雜度 \(o(n~log~n)\)

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std ;

#define int long long

#define rep(i, a, b) for (int i = (a); i <= (b); i++)

#define per(i, a, b) for (int i = (a); i >= (b); i--)

#define loop(s, v, it) for (s::iterator it = v.begin(); it != v.end(); it++)

#define cont(i, x) for (int i = head[x]; i; i = e[i].nxt)

#define clr(a) memset(a, 0, sizeof(a))

#define ass(a, sum) memset(a, sum, sizeof(a))

#define lowbit(x) (x & -x)

#define all(x) x.begin(), x.end()

#define ub upper_bound

#define lb lower_bound

#define pq priority_queue

#define mp make_pair

#define pb push_back

#define fi first

#define se second

#define iv inline void

#define enter cout << endl

#define siz(x) ((int)x.size())

#define file(x) freopen(#x".in", "r", stdin),freopen(#x".out", "w", stdout)

typedef long long ll ;

typedef unsigned long long ull ;

typedef pair pii ;

typedef vector vi ;

typedef vector vii ;

typedef queue qi ;

typedef queue qii ;

typedef set si ;

typedef map mii ;

typedef map msi ;

const int n = 100010 ;

const int inf = 0x3f3f3f3f ;

const int iinf = 1 << 30 ;

const ll linf = 2e18 ;

const int mod = 1000000007 ;

const double eps = 1e-7 ;

void print(int x)

void print(string x)

void douout(double x)

int n, m ;

struct node a[n] ;

bool cmp(node a, node b)

struct seg tr[n << 2] ;

void pushup(int x)

void pushdown(int x)

}void build(int x, int l, int r)

void modify(int x, int l, int r)

pushdown(x) ;

int mid = (l(x) + r(x)) >> 1 ;

if (l <= mid) modify(ls(x), l, r) ;

if (mid < r) modify(rs(x), l, r) ;

pushup(x) ;

}int qmid(int x, int c)

int ql(int x, int c)

int qr(int x, int c)

int query(int x)

signed main()

sort(a + 1, a + n + 1, cmp) ;

build(1, 1, m) ;

rep(i, 1, n) else

} printf("%lld\n", query(1)) ;

return 0 ;}/*

寫**時請注意:

1.ll?陣列大小,邊界?資料範圍?

2.精度?

3.特判?

4.至少做一些

1.最大值最小->二分?

2.可以貪心麼?不行dp可以麼

3.可以優化麼

4.維護區間用什麼資料結構?

5.統計方案是用dp?模了麼?

6.逆向思維?

*/

POJ1201 Intervals 線段樹 貪心

原題鏈結 題意 給定一些區間,每個區間裡必須取ci個數,這些數組成乙個集合z,求z的最少的元素數 思路 由於如果兩個區間如果有重複取的元素,那麼這個元素一定是在左邊的這個區間的最右邊的那些元素。所以我們按照區間的右邊界排序後,總是如果當前區間還需要取數,那麼就從右往左取還沒有被取的數即可。然後就可以...

IOI2018 排座位 線段樹

ioi2018seat 這題思路真的很神。原題編號從0開始,很不舒服,我們按從1開始的講。發現只需要判斷 1,i 這些數是否組成了乙個矩陣。那麼我們能不能用線段樹,第i個葉子節點存前i個數的資訊來判斷前i個數能否組成矩陣呢?有的人可能會想到第i個葉子節點維護前i個數中最左上的點和最右下的點,判斷時直...

1067 SCOI2007 降雨量 線段樹

我們常常會說這樣的話 x年是自y年以來降雨量最多的 它的含義是x年的降雨量不超過y年,且對於任意 y z x,z年的降雨量嚴格小於x年。例如2002,2003,2004和2005年的降雨量分別為4920,5901,2832和3890,則可以說 2005年是自2003年以來最多的 但不能說 2005年...