ZZUOJ 10508: 数列游戏IV
題目鏈接:http://acm.zzu.edu.cn:8000/problem.php?id=10508
題目大意:給定一個序列,長度為N,每次詢問為一組區間[Li,Ri],輸出Li到Ri中出現恰好兩次的不同數的個數.?N,M<=2*10^5,序列中元素<=10^9
解題思路:考慮用樹狀數組解決(大概是一種類型的題目)。樹狀數組一般用來快速計算更新(logN)前綴和,而對于本題來說,出現次數顯然不能單純隨意相加相減,另外,對于右區間靠前的查詢來說,對其查詢之后后面的數據更新是不會再影響到它的,因此可以離線處理,并且需要在更新的時候針對重復元素進行一些處理。
首先考慮一個元素在序列中不同位置重復出現的情況,如下:
_ x _ x _ x _ x _ x _ (下劃線表示出現了若干與x不相同的數字)
從前到后給每個x編號1,2,3,4,5,下面看一下從前到后掃面到這五個位置時如何更新(其中a,b等字母表示這個位置應當具有的值):
_ x _ x _ x _ x _ x _?
1 0
2 a ? b 那么應當有 b + a = 1, (b + a) - a = 0, 則 b = 0, a = 1?
3 a ? b ? c ?那么應當有 c + b + a = 0, (c + b + a) - (b + a) = 0, (c + b + a) - a = 1, 則 c = 0, b = 1, ?a = -1.
4 a ? b ? c ? d 那么應當有 d + c + b + a = 0, d + c + b + a - (c + b + a) = 0, (d + c + b + a) - (b + a) = 1
?(d + c + b + a) - a = 0, 則 d = 0, c = 1, b = -1, a = 0
...
即是:
_ x _ x _ x _ x _ x _?
1 0
2 1 ? 0 ?
3 ?-1 ? 1 ? 0 ??
4 0 ? -1 ? 1 ? 0
5 0 ? ?0 ? -1 ? 1 ? 0
然后關系就非常明顯了,我們只需要記錄下每個位置的數字上次出現的位置,然后 lastpos + 1,la_lastpos - 2, la_la_lastpos + 1, 即可。那么對于任意一個區間來說,由于其中每個數字都滿足互相加減的條件,因此直接樹狀數組相加減即可。
大致過程:記錄每個位置對應數字上次出現位置;將查詢的區間按照有端點排序;從1~N枚舉每個位置,按上述方法更新樹狀數組,然后計算以這個位置為右端點結束的區間的值。
代碼:
1 const int maxn = 2e5 + 10; 2 struct node{ 3 int l, r, id; 4 bool operator < (const node& t) const{ 5 return r < t.r; 6 } 7 }; 8 node range[maxn]; 9 int n, m; 10 int a[maxn], ans[maxn], bit[maxn]; 11 int last[maxn]; 12 map<int, int> mmp; 13 14 int lowbit(int x){ 15 return x & (-x); 16 } 17 void add(int x, int v){ 18 while(x <= n){ 19 bit[x] += v; 20 x += lowbit(x); 21 } 22 } 23 int sum(int x){ 24 int ans = 0; 25 while(x > 0){ 26 ans += bit[x]; 27 x -= lowbit(x); 28 } 29 return ans; 30 } 31 void solve(){ 32 memset(last, 0, sizeof(last)); 33 memset(bit, 0, sizeof(bit)); 34 for(int i = 1; i <= n; i++){ 35 last[i] = mmp[a[i]]; 36 mmp[a[i]] = i; 37 } 38 sort(range + 1, range + 1 + m); 39 int ind = 1; 40 for(int i = 1; i <= n; i++){ 41 if(last[i] != 0){ 42 int la = last[i]; 43 add(la, 1); 44 if(last[la] != 0){ 45 int lla = last[la]; 46 add(lla, -2); 47 if(last[lla] != 0) 48 add(last[lla], 1); 49 } 50 } 51 while(ind <= m && range[ind].r == i){ 52 int tml = range[ind].l, tmr = range[ind].r; 53 ans[range[ind].id] = sum(tmr) - sum(tml - 1); 54 ind++; 55 } 56 } 57 for(int i = 1; i <= m; i++){ 58 printf("%d\n", ans[i]); 59 } 60 } 61 int main(){ 62 scanf("%d %d", &n, &m); 63 for(int i = 1; i <= n; i++) 64 scanf("%d", a + i); 65 for(int i = 1; i <= m; i++){ 66 scanf("%d %d", &range[i].l, &range[i].r); 67 range[i].id = i; 68 } 69 solve(); 70 }題目:
10508: 數列游戲IV
Time Limit:?1 Sec??Memory Limit:?128 MBSubmit:?32??Solved:?6
[Submit][Status][Web Board]
Description
給定一個序列,長度為N,每次詢問為一組區間[Li,Ri],輸出Li到Ri中出現恰好兩次的不同數的個數.Input
第一行兩個整數N和M,N表示序列長度,M表示詢問次數.(N,M<=2*10^5) 第二行N個整數,表示序列.(序列中元素<=10^9) 以后M行,每行為Li和Ri,表示詢問區間.(1<=Li<=Ri<=N)?
Output
對于每組詢問,輸出一行一個整數,表示不相同數的個數.?
Sample Input
5 1 1 2 1 1 1 1 3Sample Output
1HINT
Source
Raywzy
轉載于:https://www.cnblogs.com/bolderic/p/7527223.html
總結
以上是生活随笔為你收集整理的ZZUOJ 10508: 数列游戏IV的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 陈敏敏-130242014024-实验一
- 下一篇: 基于Flask实现后台权限管理系统 -