आइए प्रक्रिया दोनों हमारे प्रश्नों और एक बाएँ से सही ढंग से हमारे तत्वों, कुछ की तरह
for (int i = 0; i < N; ++i) {
// 1. Add a[i] to all internal data structures
// 2. Calculate answers for all queries q such that r[q] == i
}
यहाँ हम इस लूप के O(N)
पुनरावृत्तियों है और हम डेटा संरचना के दोनों अद्यतन करना चाहते हैं और o(N)
समय में वर्तमान में संसाधित भाग के प्रत्यय के उत्तर का उत्तर दें।
की सरणी contains[i][j]
जो 1
प्रत्यय स्थिति i
से शुरू करता है, तो है अन्यथा संख्या j
और 0
शामिल उपयोग करते हैं। इस बात पर भी विचार करें कि हमने अलग-अलग contains[i]
अलग-अलग के लिए उपसर्ग की गणना की है। इस मामले में हम बाइनरी खोज का उपयोग करते हुए O(log N)
समय में प्रत्येक विशेष प्रत्यय क्वेरी का उत्तर दे सकते हैं: हमें केवल contains[l[i]]
सरणी में पहला शून्य मिलना चाहिए जो वास्तव में पहली स्थिति है जहां आंशिक योग इंडेक्स के बराबर है, और सूचकांक + 1 नहीं दुर्भाग्यवश, ऐसे सरणी O(N^2)
स्थान ले लेंगे और प्रत्येक अपडेट के लिए O(N^2)
समय की आवश्यकता होगी।
तो, हमें अनुकूलित करना होगा। आइए "योग क्वेरी" और "असाइनमेंट" रेंज ऑपरेशंस के साथ 2-आयामी range tree बनाएं। इस पेड़ में हम किसी भी उप-आयत पर योग से पूछ सकते हैं और O(log^2 N)
समय में किसी उप-आयत के सभी तत्वों को समान मान निर्दिष्ट कर सकते हैं, जो हमें O(log^2 N)
समय और O(log^3 N)
समय में प्रश्नों को अद्यतन करने की अनुमति देता है, समय जटिलता देता है O(Nlog^2 N + Qlog^3 N)
। अंतरिक्ष जटिलता O((N + Q)log^2 N)
(और सरणी के प्रारंभ के लिए एक ही समय) आलसी प्रारंभिकरण का उपयोग करके हासिल की जाती है।
यूपी: आइए संशोधित करें कि क्वेरी "योग" के साथ रेंज पेड़ों में कैसे काम करती है।1-आयामी पेड़ के लिए (इस जवाब बहुत लंबा नहीं बनाने के लिए), यह कुछ इस तरह है:
class Tree
{
int l, r; // begin and end of the interval represented by this vertex
int sum; // already calculated sum
int overriden; // value of override or special constant
Tree *left, *right; // pointers to children
}
// returns sum of the part of this subtree that lies between from and to
int Tree::get(int from, int to)
{
if (from > r || to < l) // no intersection
{
return 0;
}
if (l <= from && to <= r) // whole subtree lies within the interval
{
return sum;
}
if (overriden != NO_OVERRIDE) // should push override to children
{
left->overriden = right->overriden = overriden;
left->sum = right->sum = (r - l)/2 * overriden;
overriden = NO_OVERRIDE;
}
return left->get(from, to) + right->get(from, to); // split to 2 queries
}
यह देखते हुए कि हमारे विशेष मामले में पेड़ से सभी प्रश्नों उपसर्ग योग प्रश्न है, तो from
हमेशा 0
के बराबर है, इसलिए, बच्चों को कॉल में से एक हमेशा एक छोटा जवाब देता है (0
या पहले से ही गणना sum
)। तो, द्विआधारी खोज एल्गोरिदम में 2-आयामी पेड़ के लिए O(log N)
क्वेरी करने के बजाय, हम खोज के लिए विज्ञापन-प्रक्रिया प्रक्रिया को कार्यान्वित कर सकते हैं, यह get
क्वेरी के समान ही है। इसे पहले बाएं बच्चे का मूल्य प्राप्त करना चाहिए (जो O(1)
लेता है क्योंकि इसकी पहले से गणना की जा चुकी है), फिर जांचें कि नोड जो हम खोज रहे हैं वह बाईं ओर है (यह योग बाएं उपट्री में पत्तियों की संख्या से कम है) और जाओ इस जानकारी के आधार पर बाईं ओर दाईं ओर। यह दृष्टिकोण O(log^2 N)
समय (क्योंकि यह अब एक पेड़ ऑपरेशन है) के लिए क्वेरी को और अनुकूलित करेगा, जिससे O((N + Q)log^2 N))
दोनों समय और स्थान की परिणामी जटिलता दी जाएगी।
यह सुनिश्चित नहीं है कि यह समाधान Q
और N
दोनों 10^5
तक पर्याप्त तेज़ है, लेकिन शायद इसे और अनुकूलित किया जा सकता है।
क्या मैं सही हूं कि आप पहली क्वेरी को संसाधित करने से पहले सभी प्रश्नों को जानते हैं? मेरा मतलब है, बैच प्रो सारणी एल्गोरिदम ठीक है, और आपको ऑन-लाइन उत्तरों की आवश्यकता नहीं है – alexeykuzmin0
ऑफ़लाइन एल्गोरिदम ठीक है। – square1001
क्या यह किसी प्रकार का सेगमेंट पेड़ प्रकार का प्रश्न है? – Arunmu