YbtOJ 752 最優分組 笛卡爾樹,線段樹

2022-03-29 04:01:43 字數 2760 閱讀 2973

\(n\)個人,每個人有\(c_i\)和\(d_i\)分別表示這個人所在的隊伍的最少/最多人數。

然後要求將這些人分成編號連續的若干隊使得隊伍最多,並且求分隊方案數。

\(1\leq n\leq 10^6\)

陰間題目...

為了方便計算先定義乙個結構體(包含答案和方案數)和加法運算表示取最大值/相同加方案數作為答案。

設\(f_i\)表示以第\(i\)個作為末尾的答案,首先\(d_i\)就相當於限制了乙個字尾的範圍,所以可以先用單調佇列算出\(left_i\)表示根據\(d\)的限制從\(i\)能選到的最左位置\(-1\)。

然後\(c_i\)的限制很陰間,因為它的限制顯然不是乙個連續的範圍。

考慮到乙個\(l\sim r\)的轉移的\(c\)限制只由這個區間最大的\(c_i\)來限制,所以可以考慮在笛卡爾樹上做。這樣其實加個資料結構可以輕鬆做到\(o(n\log^2 n)\),但是這樣過不了,還得優化。

分類討論一下,我們現在考慮乙個在右邊的\(i\)和乙個在左邊的\(j\),我們已經處理好了左邊的答案,要用它來更新右邊的。

\(left_i且\(i< mid+c_\),此時可以先不管\(left\)了,只需要考慮後面那個,而且注意到每次\(i\)移動一格後\(j\)會多乙個取值位置,所以我們維護乙個記錄區間最優答案的線段樹。然後先用線段樹查詢出第乙個滿足條件的\(i\)的答案,然後後面每次加乙個答案就好了

然後這裡一次的複雜度是左右區間的最小長度,和啟發式合併類似時間複雜度\(o(n\log n)\)

\(left_i且\(i\geq mid+c_\),此時對於所有的\(i\),\(j\)的取值範圍都是\([l,mid-1]\),直接拿線段樹查出最大的答案,然後向右邊區間修改就好了。

\(l\leq left_i,這個好像只能對於每個\(i\)用線段樹暴力查詢。但是可以注意到,對於每個\(i\)從頭到尾只會到一次這種情況,所以時間複雜度還是\(o(n\log n)\)的

\(left_i\geq mid\),這個向右邊分治的時候會解決,不需要這裡統計

上面這四個情況的都是在乙個區間裡的,而且是按順序出現的,需要注意的時候第二種情況是不能暴力列舉的,所以我們需要二分出這個情況區間的末尾

然後總共的時間複雜度就是\(o(n\log n)\)的了,細節有點多,比如建笛卡爾樹的時候還要用\(st\)表查區間最大之類的。

#pragma gcc optimize(2)

%:pragma gcc optimize(3)

%:pragma gcc optimize("ofast")

%:pragma gcc optimize("inline")

#include#include#include#include#include#define ll long long

using namespace std;

const ll n=1e6+10,p=1e9+7,nul=-1e9+6;

struct node

};node operator+(node x,node y)

void change(ll x,ll l,ll r,ll l,ll r,node p)

ll mid=(l+r)>>1;downdata(x,l,r);

if(r<=mid)change(x*2,l,mid,l,r,p);

else if(l>mid)change(x*2+1,mid+1,r,l,r,p);

else change(x*2,l,mid,l,mid,p),change(x*2+1,mid+1,r,mid+1,r,p);

w[x]=w[x*2]+w[x*2+1];

} void set(ll x,ll l,ll r,ll pos,node p)

ll mid=(l+r)>>1;downdata(x,l,r);

if(pos<=mid)set(x*2,l,mid,pos,p);

else set(x*2+1,mid+1,r,pos,p);

w[x]=w[x*2]+w[x*2+1];

} node ask(ll x,ll l,ll r,ll l,ll r)

}t;ll ask(ll l,ll r)

void solve(ll l,ll r)

ll x=ask(l+1,r);

solve(l,x-1);

ll l=x,r=r;

while(l<=r)

ll pos=r;

l=max(x,l+c[x]);

r=min(min(r,x+c[x]),pos);

node tmp=t.ask(1,0,n,l,l-c[x]);

ll p=l-c[x]+1;

for(ll i=l;i<=r;i++)

solve(x,r);

return;

}signed main()

st[i][0]=i;

} for(ll i=2;i<=n;i++)lg[i]=lg[i>>1]+1;

for(ll j=1;(1<=c[y])?x:y;

} for(int i=0;i<(n<<2);i++)

t.lazy[i]=node(nul,0),t.w[i]=node(-1e9,0);

for(ll i=1;i<=n;i++)f[i]=node(nul,nul);

f[0]=node(0,1);solve(0,n);

if(f[n].f<=0)return 0&puts("-1");

printf("%lld %lld\n",f[n].f,f[n].g);

return 0;

}

YbtOJ 752 最優分組

首先我們忽略 c 的限制,如果只考慮 d 的話,不難發現以第 i 個人結尾分組,左端點是一段連續的區間 lft i,i 可以用 st 表預處理區間最小值然後雙指標掃一遍求出 lft 加入了 c 的限制之後,此時和每乙個點能轉移過來的位置就不是連續的區間了。考慮對於當前區間內 c 的最大值分治,也就是...

752 開啟轉盤鎖

題目描述 你有乙個帶有四個圓形撥輪的轉盤鎖。每個撥輪都有10個數字 0 1 2 3 4 5 6 7 8 9 每個撥輪可以自由旋 例如把 9 變為 0 0 變為 9 每次旋轉都只能旋轉乙個撥輪的一位數字。鎖的初始數字為 0000 乙個代表四個撥輪的數字的字串。列表 deadends 包含了一組死亡數字...

752 開啟轉盤鎖

你有乙個帶有四個圓形撥輪的轉盤鎖。每個撥輪都有10個數字 0 1 2 3 4 5 6 7 8 9 每個撥輪可以自由旋 例如把 9 變為 0 0 變為 9 每次旋轉都只能旋轉乙個撥輪的一位數字。鎖的初始數字為 0000 乙個代表四個撥輪的數字的字串。列表 deadends 包含了一組死亡數字,一旦撥輪...