Ynoi2013 對資料結構的愛

2022-06-15 12:36:11 字數 3668 閱讀 5783

\(\texttt \)

修正了一些語言描述上的錯誤,以及對於一些地方使用了更好的表述。

\(\texttt \)

修正了一些公式和語言描述上的錯誤。

對於當前要經過的一段區間 \(x\),若給定不同初始值的 \(sum\) ,其減 \(p\) 的次數 \(k\) 也不同,那麼 \(k\) 和 \(sum\) 是否滿足什麼關係呢?

意淫一下感覺是非遞減,看能不能寫清楚。

考慮增大 \(sum\) ,此時雖然模 \(p\) 的位置並不確定,但是 \(k\) 要麼不變,要麼增大,理解一下這個過程中取模位置和次數的變化:

先掃過去,對於 \(i\in[r_1,r_2)\) 這段範圍內的數,過程量 \(sum_i\) 都增加 \(num\)(增大的量),原來滿足 \(sum_i這個條件的有:\(sum_i+num。

然後 \(r_2\) 這個位置滿足:\(sum_且 \(sum_+num\ge p\),此時 \(r_2\) 這個位置取模。

這時有兩種情況:

\(1.\ num-p\ge 0\)

對於 \(i\in[r_2,r_3)\) 這段範圍內的數,過程量 \(sum_i\) 都增加 \(num-p\),後同上。

\(2.\ num-p<0\)

實際上這個子問題就是 \(num<0\) 的實現。

此時後面一段的過程量減小,直到乙個原本應當進行一次減 \(p\) 操作的位置不減了,而這個情況可以看做兩個位置是否取模的真假(true or false)交換,可以保證 \(k\) 不變。然後後面的又是變成跟原來一樣的子問題。

但也有可能後面沒有這個真變假(原本取模變成不取模)的位置,也就是減 \(p\) 次數變為 \(k+1\)。

綜上可得:對於每個使 \(k+1\) (對 \(k\) 產生了 \(1\) 的貢獻)的位置,最多有乙個使 \(k-1\) 的位置與其對應,所以 \(k\) 隨 \(sum\) 非嚴格單調遞增。

接下去就是考慮實現了:

\(k\) 隨 \(sum\) 非嚴格單調遞增,同時有 \(0\le k\le lens\),其中 \(lens\) 為塊長。

更嚴格來說,設原來的取模次數為 \(sl\),有:

\[\begin sl\le k\le lens,sum>0\\ 0\le k\le sl,sum<0\end

\]這裡 \(k\) 和 \(sum\) 的關係十分相似於函式 \(y=\left\lfloor x \right\rfloor\)。

那麼如果二分 \(sum\) 的話,掃過這個塊模擬,就可以得到這個 \(sum\) 對應的 \(k\)。

也就是說可以預處理出對於每個 \(k\),最小的 \(sum\) 值,設這個東西為 \(f(k)\),特殊的,\(f(0)=-\infty\)。

那麼如果前面的值處理好了傳給當前區間,就可以求出對應的 \(k\) 然後加上這段區間的和,減去取模次數乘模數。

分塊的話:

預處理複雜度:\(\operatorname o(\log(2e9\times lens)\times n)\) 乘上乙個常數。

查詢的複雜度:\(\operatorname o(m\times \frac\times \log lens)\)

最優的極限複雜度為:\(1.3\times 10^\)。

分塊打了,炸了,一分都騙不到

看了題解,不是傻傻地每一塊單獨算,而是「合併」左右子樹的資訊。

處理好自己左子樹和右子樹的資訊以後,設當前節點為 \(i\),左兒子為 \(l\),右兒子為 \(r\)。

\[f_=\min(f_,\max(f_,f_+p\times x-add_l)

\]其中,\(add_l\) 表示 \(l\) 這段區間的和,\(x,y\) 分別為左右區間取模的次數。

關於@81179332_ 提到的 \(c_-c_x\ge p\) 的證明(在我這裡寫成 \(f_-f_x\ge p\)),我的理解是:

因為 \(f_x\) 對應的是取模 \(x\) 次 \(sum\) 的最小值,那麼當 \(sum=f_x-1\),有乙個本來要取模的位置不取模了,所以只可能是某個過程量在 \(sum=f_x\) 的時候剛好等於 \(0\)(即這步操作減 \(p\) 以後等於 \(0\))。

當 \(sum=f_x+p-1\) 時,可以用前面 \(\operatorname a\) 的處理過程結合上一段來理解,\(k\) 一定等於 \(x\),所以相鄰兩個值的差至少為 \(p\)。

那麼對於上面的式子來說,有:

\[\begin f_+p\times x-add_l\ge f_+ p\times (x+1)-add_l \\ f_+p\le f_\end

\]並且乙個合法的二元組 \((x,y)\) 需滿足:\(f_-1+add_l-p\times x\ge f_\)

對於乙個常數 \(k=x+y\),若 \(k\) 已經被 \((x,y)\) 更新過了,那麼 \((x+1,y-1)\) 還有存在的必要嗎?

由前面可以得到:

\[f_+p\times x-add_l\le f_+p\times x-add_l\le f_-1

\]那麼此時對於二元組 \((x+1,y-1)\),\(\max(f_,f_+p\times x-add_l)\) 取得一定是前面的那項。

前面那項和 \(y\) 又沒有任何關係,所以有:

如果存在合法二元組 \((x,y)\),則 \((x+1,y-1)\) 對答案無貢獻。(當然,要考慮 \(x\) 的貢獻。即如果 \((x,|r|)\) 符合二元組,為了讓 \(x+1\) 的貢獻能夠更新 \(f_\),之後 \((x+1,|r|)\) 也得考慮)

注:這裡的 \(|r|\) 指區間 \(r\) 的長度,即 \(y\) 的最大取值。

那麼就可以雙指標使得複雜度正確了。

#include #define ll long long

using namespace std;

const int man=1e6+3;

const ll max=1e18;

struct milk

t[man<<1];

int nam;

int n,m;

ll ans=0;

ll p;

int rin()

inline void make_tree(int l,int r,int i)

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

t[i].ls=++nam;make_tree(l,mid,nam);

t[i].rs=++nam;make_tree(mid+1,r,nam);

t[i].sum=t[t[i].ls].sum+t[t[i].rs].sum;

for(int _=l;_<=r+3;_++)t[i].q.push_back(max);

l=t[i].ls;r=t[i].rs;

int x,y;

x=y=0;

for(;x<=t[l].lens;x++)

ans+=t[i].sum;

ans-=p*last;

return;

}if(t[i].ls==0)return;

if(t[t[i].ls].r>=l)cheak(l,r,t[i].ls);

if(t[t[i].rs].l<=r)cheak(l,r,t[i].rs);

return;

}int main()

return 0;

}

C 資料結構實驗1 1 對陣列進行簡單操作

輸入陣列,獲取第乙個元素 最後乙個元素 重定義 和 來進行對陣列的運算 2018資料結構實驗1.1 陣列操作 include using namespace std templateclass set class array public set 初始化函式 array array 插入元素 voi...

資料結構 用C語言實現對陣列的操作

今天開始複習資料結構了,先從對陣列的操作開始。include include include struct arr 定義陣列的資料結構 void init struct arr array,int len 初始化陣列,使其擁有一塊確切的記憶體 bool is empty struct arr arr...

對核心資料結構的同步訪問

可以使用前面所述的同步原語保護共享資料結構避免競爭條件。當然,系統效能可能隨所選擇同步原語種類的不同而有很大變化。通常情況下,核心開發者採用下述由經驗得到的法則 把系統中的併發度保持在盡可能高的程式。系統中的併發度又取決於兩個主要因素 同進運轉的 i o裝置數 進行有效工作的 cpu數 為了使i o...