\(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 包含了一組死亡數字,一旦撥輪...