又是lxl..(太好了,重復的題直接跳過了= =)
T1
給定一個序列,查詢區間小於等於 \(k\) 的元素的個數。
solution
每個位置開個 \(vector\) 把詢問兩個端點離線下來。
然后維護一個值域樹狀數組,每個詢問差分一下就好了。
T2
給定一個二維平面,上面有 \(n\) 個矩形,每個矩形的坐標為 \([1, n]\)
有 \(m\) 次查詢,每次查詢一個二維平面上的點被多少矩形覆蓋。
solution
掃描線。
把詢問離線下來,按照 \(x\) 排序。
然后以 \(y\) 軸為下標建一棵線段樹,然后一條掃描線從左到右掃,如果掃到一個矩形的左端點就區間加,如果掃到一個矩形的右端點就區間減,查詢就是單點查詢了。
T3
solution
把詢問離線下來,按照右端點排序。
對於每個詢問,如果有顏色出現了多次,那么我們只關心它出現的最后依次就好了。
記錄每個顏色上次出現的位置,然后開個樹狀數組,記錄顏色的數量,對詢問差分一下就好了。
/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
const int N = 1e6 + 5;
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
map<int, int>last;
int n, tree[N], a[N], m, Ans[N], p = 1;
struct Node{int l, r, id;}A[N];
void Insert(int x, int k) {
for (int i = x; i <= n; i += i & (-i)) tree[i] += k;
}
int Query(int x) {
int res = 0;
for (int i = x; i; i -= i & (-i)) res += tree[i];
return res;
}
bool cmp(Node x, Node y) {
if (x.r == y.r) return x.l < y.l;
return x.r < y.r;
}
int main(){
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
m = read();
for (int i = 1; i <= m; i++)
A[i].l = read(), A[i].r = read(), A[i].id = i;
sort(A + 1, A + m + 1, cmp);
for (int i = 1; i <= m; i++) {
while (p <= A[i].r) {
if (last[a[p]]) Insert(last[a[p]], -1);
Insert(last[a[p]] = p, 1);
p++;
}
Ans[A[i].id] = Query(A[i].r) - Query(A[i].l - 1);
}
for (int i = 1; i <= m; i++) printf("%d\n", Ans[i]);
puts("");
return 0;
}
T4
給定長為 \(n\) 的序列, \(m\) 次查詢區間有多少個值只出現一次。
solution
十分妙的二維平面轉化。
對於一次查詢 \([l, r]\) 中,一個數如果有貢獻,那么左邊第一個與它相同的數 < l 並且右邊第一個與它相同的數 > r。
建立一個二維平面,橫坐標為右端點,縱坐標為左端點,然后每個點就對應着一個詢問。
一個點能產生貢獻的范圍在二維平面上就是一個矩形。
這樣問題就轉化為了二維平面上一個點被多少個矩形覆蓋的問題了。
T5
給定一個長為 \(n\) 的序列,常數 \(w\) ,進行 \(m\) 次查詢
每次查詢給出的區間 \([l,r ]\) ,詢問區間內是否存在兩個數和為 \(w\) 。
solution
對於位置 \(i\) ,找到左邊第一個滿足 \(w - a[i] = a[j]\) 的 \(j\) , 此時 \(pre[i] = j\) ,對於查詢區間內的所有數,如果都滿足 \(pre[i]<l\) 那么這個區間就是不合法的。
現在就是求一段區間 \([l,r]\) 內的所有的數,是否所有的數都滿足 \(pre[i] < l\) 。
怎么求?
求區間 \(pre[i]\) 的最大值,然后與 \(l\) 判斷一下就好了。
\(RMQ\) 實現。
T6
給定一個長為 \(n\) 的序列,有 \(m\) 次詢問。
每次查詢區間內出現出現次數為奇數的異或和。
solution
。。。。
一時沒反應過來 = =
區間出現偶數次的數異或都抵消了,這個題也就成了求區間的異或和。
T7
給定一個序列,每次查詢區間中出現偶數次的數的異或和。(每種數只算一遍)
solution
區間出現奇數次的數的異或和已經會求了,那么出現次數為偶數的數的異或和就是出現過的數的異或和 ^ 出現次數為奇數的數的異或和。(出現次數為奇數的兩次異或抵消了,剩下的只剩出現次數為偶數次的了。)
T8
給定二維平面上的 \(n\) 個矩形,矩形的坐標為 \([1, n]\) 內,求矩形的面積並。
solution
豎着維護一個掃描線,如果掃到一個矩形的左端點,就區間+1,如果是右端點就區間 -1,每走一步都要查詢一下全局中大於 \(0\) 的個數加入答案,這樣就退化到了暴力。可以維護區間等於 \(0\) 的個數,然后用全局減去 0 的個數就好了。
維護區間等於 \(0\) 的個數?
每個數都不可能小於 0,所以可以維護最小值,然后維護一個最小值的個數,判斷最小值是否為 0,如果不是那就沒有 0,否則最小值個數就是 0 的個數。
復雜度:\(O(n logn)\)
T9
維護一堆不等式
- 插入一個 \(ax + b > c\) 的不等式
- 刪除第 \(i\) 個插入的
- 查詢 \(x =k\) 的時候成立的不等式個數
solution
將不等式變形
當 \(a > 0\) 時,為 \(x > \frac{b - c}{a}\)
當 \(a < 0\) 時,為 \(x < \frac{b - c}{a}\)
當 \(a = 0\) 時,為 \(b > c\)
樹狀數組維護就好了。
T9
Codechef DGCD
給定一個長度為 \(n\) 的序列,有 \(m\) 次操作:
- 將區間加上 x
- 查詢區間的最大公倍數
solution
\(gcd(a, b) = gcd(a - b, b)\)
\(gcd(a_l, a_{l + 1}, a_{l + 2} \dots a_{a_r}) = gcd(a_l, a_{l + 1} - a_l ,a_{l + 2} - a_{l + 1}\dots a_{r} - a_{r - 1})\)
另 \(b_i = a_i - a_{i - 1}\)
那么也就成了求 \(gcd(a_l, b_{l + 1}, b_{l + 2}\dots b_{r})\)
區間修改。
當在 \(a\) 數組對 \([l,r]\) 區間更改的時候,\(b\) 數組改變的只有 \(b_{l}\) 和 \(b_{r + 1}\) (手模一下)
然后區間修改就轉化成立兩個單點修改。
T10
維護一個長為 \(n\) 的字符串序列,有 \(m\) 次操作:
- 單點修改。
- 查詢兩個區間所對應的字符串是否完全一樣。
solution
線段樹維護區間的 \(hash\) 值,預處理出 base 數組,然后就可以區間合並了,單點修改直接修就好了。
T11
維護一個長為 \(n\) 的字符串序列。
- 單點修改。
- 查詢兩個區間所對應的字符串的 \(LCP\) 的長度
\(LCP:\) 兩個字符串的最長公共前綴
solution
求 \(lcp\) 可以二分其長度,每次二分完判斷 \(hash\) 值是否相等。
T12
給兩個字符串 \(A,B\) ,長度分別為 \(n\) ,\(m\) 。
有 \(q\) 次操作
每次修改 \(B\) 的一個位置,或者查詢 \(A\) 中有多少長為 \(m\) 的字串與 \(B\) 完全相同。
solution
\(O(n)\) 已處理出 \(A\) 串中長 \(m\) 的所有字串的 \(hash\) 值,用 \(map\) 存起來,然后用樹狀數組維護 \(B\) 的區間 \(hash\) 值,然后在 \(map\) 中插就好了。
T13
給出一棵樹,\(n\) 個節點,\(m\) 次修改,每次給定 \(s\) 和 \(t\) ,把 \(s\) 到 \(t\) 路徑上的點權都 +1,問 \(m\) 次操作后的最大點權
solution
對詢問進行離線,然后樹上差分就好了。
T14
給一個 \(n\) 個點的樹,有點權,有 \(m\) 次查詢,每次給出三個數 \(x, y, z\) ,求 \(x\) 到路徑上有多少個點值 = z
solution
樹上差分,先 \(dfs\) 一遍,每個點開個 \(map\) 把該點的權值在 \(map\) 中 +1,然后就可以求到一個點到根的路徑上有多少個值等於 \(z\) ,求路徑就差分一下就好了。
T15
給一個 \(n\) 個點的數,有點權,有 \(m\) 次查詢,每次給三個數 \(x,y,z\) 求 \(x\) 到 \(y\) 之間有多少點值 < z;
solution
和上題一個樣差分,因為求小於 \(z\) 的個數,用樹狀數組維護就好了。
T16
給一棵 \(n\) 個節點的樹,有 \(m\) 次詢問。
每次查詢;
從 \(x\) 點開始向 \(y\) 走,每秒走一步
假設在第 \(i\) 秒走到了 \(i\) 點,則答案 +1.
solution
天天愛跑步
樹上差分。
考慮 \(x\) 到 \(i\),如果 \(i\) 對答案有貢獻,那么一定滿足 \(dep_x - dep_i = i\) ,
從 \(y\) 到 \(i\) ,如果 \(i\) 有貢獻一定滿足 \(s - (dep_y - dep_i) = i\) \(s\) 為路徑 \(x\) 到 \(y\) 長度。
這樣差分一下就能做了。
T17 loj 6276
樹,點有顏色,求多少條樹上簡單路徑滿足上面的顏色互不相同。
每種顏色出現次數 \(\leq 20\), \(n\leq 10^5\)
solution
如果有兩個相同顏色的點,一條合法的路徑兩個端點一定不會分別在這兩個點的子樹內,選了構成的路徑一定經過這兩個顏色相同的點。
對於子樹考慮 \(dfs\) 序處理。
兩個相同顏色端點(不互為祖先)的子樹都分別對應着 \(dfs\) 的一個區間,如果路徑的左右端點在這個分別在這兩個區間的話那么這條路徑就不合法。
如果一個點與它一個祖先顏色相同,那么不合法的路徑就是從該點的子樹內選一個點,祖先的子樹外選一個點,對應到 \(dfs\) 序上就是一段連續的區間和兩端區間。
構造二維平面(一維為左端點,一維為右端點),每條路徑都對應這二維平面內的一個點,每兩個顏色相同的不互為祖先的點能形成的不合法的路徑都是一個矩形,互為祖先的就形成兩個矩形。
初始最大的矩形(所有的路徑)值為 1,然后將構成的每個小矩形設為 0,最后統計有多少個 1 就好了
T18
給出一棵 \(n\) 個點的樹,以及一個長為 \(n\) 的序列 \(a\),\(a_i\) 表示 \(a\) 序列 \(i\) 位置為一個樹上編號為 \(a_i\) 的節點,樹的邊權為 \(1\)
有 \(m\) 次詢問,每次詢問給兩個 \(a\) 的區間,求從兩個區間中各選出一個點能得到的樹上最遠距離。
就是從 \([l_1,r_1]\) 中選一個 \(i\),\([l_2,r_2]\) 中選一個 \(j\),求 \(max~~ dist(a[i],a[j])\)
\(n\leq 10^5,m\leq 10^6\)
solution
樹上的一個點 \(x\) 和一個點集,求找出點集中的一個點到 \(x\) 的路徑長度最長:在點集中找出一個直徑,然后所求就是兩個端點中的較大一個。
根據這個性質可以線段樹維護區間直徑:新的直徑的兩個端點一定在左區間直徑的兩個端點和右區間直徑的兩個端點。
最后查詢給出的兩個區間的直徑,與合並一樣,枚舉一下就好了;
距離可以用 ST 表實現。
T19
給出一個 \(n\) 個點的樹,有點權
有 \(m\) 次查詢,每次會把 \(x\) 的點權修改,或者查詢 \(x\) 所在子樹內的點權和。
solution
\(dfs\) 序上單點修改,查區間和。
T20
給一個 \(n\) 個點的樹,有點權
有 \(m\) 次查詢,每次會修改一個點的權值,或者查 \(x\) 到 \(y\) 路徑上的點權和。
solution
樹剖
樹上差分。
單點修改查詢點到根路徑的點權和。
一個點修改,影響的只有其子樹內的點,所以單點修改可以改為對 \(dfs\) 序上對它的子樹進行區間修改。
T21
給定一棵 \(n\) 個點的樹,以及一個初始為空的集合,集合里的數據類型為二元組
有 \(m\) 次操作
每次操作可能是在集合中插入一個二元組 \((x,y)\),刪除一個二元組,或者查詢樹上一條邊,是否滿足,對所有集合中的二元組 \((a,b)\),\(a\) 到 \(b\) 的簡單路徑都經過了這條邊,即假設刪掉這條邊,是否會將集合中所有二元組都分到了兩個部分
solution
每插入一個二元組相當於路徑 +1,刪掉就是路徑 -1,然后就是單點查詢了。
T22
給定一個正數序列,輸出前 \(k\) 小的子區間和。
solution
顯然最小的一定是區間 \([1,1],[2,2],[3,3]\dots[n,n]\) 中最小的一個,把它們加入一個堆中。
因為區間的數都是正數,所以 \([l,r] < [l - 1, r],[l, r + 1]\) 。
依次取出 \(k\) 個數,取出一個 \([l, r]\) 就加入 $[l-1,r] $ 和 \([l, r + 1]\),因為加入的區間可能重復,\(map\) 去重就好了。