D. Cut and Stick
(賽后補題)借本題學習莫隊算法以及區間眾數的求法
題意:對于整型數組,每次詢問[L,R][L,R][L,R]區間問最少分為多少個子序列,使得每個子序列的眾數xxx的個數cntxcnt_xcntx?不大于 ?len2?\left \lceil \frac{len}{2} \right \rceil?2len??,lenlenlen表示子序列的長度
思路:對于每次詢問只需知道詢問區間內的眾數xxx的個數即可,最優解即為將其余的非眾數盡可能與更多的xxx組合為一個子序列,而剩下的xxx每個自為一個子序列
給一個不嚴格的證明,圖中有aaa個眾數與bbb個非眾數,法①將全部的非眾數放在同一個子序列中,此時該子序列最多匹配b+1b+1b+1個眾數,而剩余的眾數則需單獨出現;法②將b個非眾數分為兩部分(c+d=bc+d=bc+d=b),兩部分分別匹配c+1c+1c+1和d+1d+1d+1個眾數(c+1+d+1=c+d+2=b+2c+1+d+1 = c+d+2 = b+2c+1+d+1=c+d+2=b+2)此時比法①多匹配了一個眾數,然而由于被分為兩部分,自然地也多產生了一個子序列,因此兩者是等效的,可見只要將盡可能多的眾數匹配給非眾數,無論非眾數分為多少塊其效果是等效的
因此問題便轉化為求區間眾數的個數問題。
數組長度:nnn,詢問次數:mmm
若采用暴力求解,每次詢問計算眾數的個數的時間復雜度為O(n)O(n)O(n),總時間復雜度為O(m?n)O(m*n)O(m?n),會超時
采用莫隊算法優化,可將每次詢問的平均時間復雜度降為O(n)O(\sqrt{n})O(n?)
莫隊步驟:
將數組分為大小為n\sqrt{n}n?的塊將詢問順序按下面的規則排序
首先按照LLL所在的塊號排序,塊號越小越靠前(升序)
若LLL所在的塊號相同,則按RRR所在的塊號升序接著定義兩個函數add()add()add()和del()del()del()分別用于添加和刪除元素,用于在上一個詢問的區間基礎上增刪元素得到當前詢問的區間,每次調用增刪函數的關鍵是動態地更新眾數的狀態
維護眾數狀態:
采用mapmapmap映射存儲每個數出現的次數,采用數組aaa記錄有多少個數出現了iii次,當前的眾數的個數即為數組a[i]>0a[i]>0a[i]>0的最大的iii,每次增刪實時更新兩者的數據即可
#include<bits/stdc++.h>
#define debug1(x) cout<<#x<<":"<<x<<endl
#define fastio() ios_base::sync_with_stdio(0);cin.tie(0)
typedef long long ll
;
typedef unsigned long long ull
;
const int N
= (int)3e5+100;
const int RN
= sqrt(N
)+10;
const int MOD
= 10000;
using namespace std
;
int a
[N
];
map
<int,int> mp
[RN
];
int n
,m
,block
;
int cnt
[N
],sum
[N
],ans
[N
],max_cnt
= 0;
struct query
{int id
;int l
;int r
;
}q
[N
];bool cmp(query a
, query b
)
{int al
= a
.l
/ block
, bl
= b
.l
/ block
;int ar
= a
.r
/ block
, br
= b
.r
/ block
;if(al
!= bl
) return al
< bl
;return ar
< br
;
}void add(int e
)
{sum
[cnt
[e
]]--;cnt
[e
]++;sum
[cnt
[e
]]++;max_cnt
= max(max_cnt
, cnt
[e
]);
}void del(int e
)
{sum
[cnt
[e
]]--;if(cnt
[e
] == max_cnt
&& sum
[cnt
[e
]] == 0) max_cnt
--;cnt
[e
]--;sum
[cnt
[e
]]++;
}int main()
{cin
>>n
>>m
;block
= sqrt(n
);for(int i
= 1; i
<= n
; i
++) scanf("%d",&a
[i
]);int l
, r
;for(int i
= 0; i
< m
; i
++){scanf("%d%d",&l
,&r
);q
[i
].id
= i
;q
[i
].l
= l
;q
[i
].r
= r
;}sort(q
, q
+ m
, cmp
);int cl
, cr
, width
;cl
= cr
= 0; add(a
[0]);for(int i
= 0; i
< m
; i
++){l
= q
[i
].l
, r
= q
[i
].r
;while(cr
< r
) add(a
[++cr
]);while(cl
> l
) add(a
[--cl
]);while(cr
> r
) del(a
[cr
--]);while(cl
< l
) del(a
[cl
++]);width
= r
- l
+ 1;ans
[q
[i
].id
] = max(2 * max_cnt
- width
, 1);}for(int i
= 0; i
< m
; i
++) printf("%d\n", ans
[i
]);return 0;
}
總結
以上是生活随笔為你收集整理的【莫队】区间众数(Codeforces Round #716 (Div. 2) D)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。