class NumMatrix {
int[][] preSum;
public NumMatrix(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
preSum = new int[m + 1][n + 1];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
preSum[i + 1][j + 1] = preSum[i + 1][j] + preSum[i][j + 1] - preSum[i][j] + matrix[i][j];
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
return preSum[row2 + 1][col2 + 1] - preSum[row2 + 1][col1]
- preSum[row1][col2 + 1] + preSum[row1][col1];
}
}
public int maxSubArrayLen(int[] nums, int k) {
int n = nums.length;
int[] pre = new int[n + 1];
//k: 存储当前的前缀和的值pre[i], v:存储当前前缀和下最小的下标索引
Map<Integer, Integer> map = new HashMap<>();
map.put(pre[0], 0);
for (int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] + nums[i - 1];
if (!map.containsKey(pre[i])) {
map.put(pre[i], i);
}
}
int maxLen = 0;
//遍历到的为 pre[i] ,如果在 map 中存在 pre[i]-k ,说明存在一个长度为 k 的子数组,
//现在我们得找到这个子数组的起始索引,即 map.get(pre[i]-k),
// 于是我们统计从 map.get(sum[i]-k) 到 i-1 长度,并更新 maxLen
//倒序遍历 [0...i]的长度如果比maxLen小,没必要继续遍历
for (int i = n; i > maxLen; i--) {
int t = pre[i] - k;
if (map.containsKey(t)) {
maxLen = Math.max(maxLen, i - map.get(t));
}
}
return maxLen;
}
public int findMaxLength(int[] nums) {
//0 相当于-1 -1 +1 cnt值维持在0附近波动
//k 表示计数0的次数,v表示当前计数下,0的最早的下标索引
//哈希表表示每一个前缀和第一次出现时的下标索引
Map<Integer, Integer> map = new HashMap<>();
int res = 0, cnt = 0;
map.put(0, -1);
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 1) cnt++;
else if (nums[i] == 0) cnt--;
if (map.containsKey(cnt)) {
int prev = map.get(cnt);
res = Math.max(res, i - prev);
} else {
map.put(cnt, i);
}
}
return res;
}
public int findMaxLength(int[] nums) {
int res = 0, n = nums.length;
int[] preSum = new int[n + 1];
for (int i = 0; i < n; i++) {
preSum[i + 1] = preSum[i] + (nums[i] == 0 ? -1 : 1);
}
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i <= n; i++) {
if (map.containsKey(preSum[i])) {
res = Math.max(res, i - map.get(preSum[i]));
} else {
map.put(preSum[i], i);
}
}
return res;
}
public int subarraySum(int[] nums, int k) {
int res = 0;
//k:存储的是前缀和 [0...i]的元素的前缀和,v:该前缀和出现的次数
Map<Integer, Integer> dict = new HashMap<>();
dict.put(0, 1);
int preSum = 0;
for (int i = 0; i < nums.length; i++) {
preSum += nums[i];
//preSum[i]-preSum[j] =k 相当于在[0...i]中找有多少个j满足条件
int t = preSum - k;
if (dict.containsKey(t)) res += dict.get(t);
dict.put(preSum, dict.getOrDefault(preSum, 0) + 1);
}
return res;
}
public boolean checkSubarraySum(int[] nums, int k) {
int n = nums.length;
int[] preSum = new int[n + 1];
//k存储的是preSum[i]%k的值,v是下标索引
Map<Integer, Integer> map = new HashMap<>();
for (int i = 1; i <= n; i++) {
preSum[i] = preSum[i - 1] + nums[i - 1];
map.put(preSum[i] % k, i - 1);
}
for (int i = 0; i <= n; i++) {
int t = preSum[i] % k;
//存的是下标 [0,1]距离为2 可能下标的结果小
if (map.containsKey(t) && Math.abs(map.get(t) - i + 1) >= 2) return true;
}
return false;
}
public int pivotIndex(int[] nums) {
int n = nums.length;
int[] pre = new int[n + 1];
for (int i = 0; i < n; i++) pre[i + 1] = pre[i] + nums[i];
for (int i = 0; i < n; i++) {
if (pre[i] == pre[n] - pre[i + 1]) return i;
}
return -1;
}
public int minFlipsMonoIncr(String s) {
int n = s.length();
//zero 统计0的前缀和 one 统计1的前缀和
int[] zero = new int[n + 1], one = new int[n + 1];
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
if (c == '0') {
zero[i + 1] = zero[i] + 1;
one[i + 1] = one[i];
} else {
one[i + 1] = one[i] + 1;
zero[i + 1] = zero[i];
}
}
int res = s.length();
for (int i = 1; i <= n; i++) {
//前部分1的个数 后部分0的个数
int pre_one = one[i], suc_zero = zero[n] - zero[i];
//将前部分的1都变成0 后部分的0都变成1 形如00000111这样的结果
res = Math.min(res, pre_one + suc_zero);
//前部分0的个数
int pre_zero = zero[i];
//将前部分的0都变成1 后部分的0都变成1 形如111111111这样的结果
res = Math.min(res, pre_zero + suc_zero);
}
return res;
}
public int minFlipsMonoIncr(String S) {
int N = S.length();
int[] P = new int[N + 1];
for (int i = 0; i < N; ++i)
P[i+1] = P[i] + (S.charAt(i) == '1' ? 1 : 0);
int ans = Integer.MAX_VALUE;
for (int j = 0; j <= N; ++j) {
ans = Math.min(ans, P[j] + N-j-(P[N]-P[j]));
}
return ans;
}
//[i]的状态只和[i-1]即前一个状态有关,可以进行空间优化
public int minFlipsMonoIncr(String s) {
int n = s.length();
//当前下标i下字符为0和1的情况下,使得s[0...i]单调递增的最小翻转次数
int f0 = 0, f1 = 0;
for (int i = 0; i < n; i++) {
//下一轮的 f0 f1
int _f0 = f0, _f1 = Math.min(f0, f1);
if (s.charAt(i) == '1') {//当前下标i的字符是'1'
_f0++;
} else {
_f1++;
}
f0 = _f0;
f1 = _f1;
}
return Math.min(f0, f1);
}
public int minFlipsMonoIncr(String s) {
int n = s.length();
//维护一个单调递增的tails数组 形如 00011
char[] tails = new char[n];
int index = 0;//tails数组当前的处理到的位置
for (char x : s.toCharArray()) {
int i = 0, j = index;//
while (i < j) {
int mid = i + (j - i) / 2;
//如果当前的x比tails[mid]大,说明应该放在mid的右侧位置,否则放在左侧位置
if (tails[mid] <= x) i = mid + 1;
else j = mid;
}
tails[i] = x;//x放置的位置
//如果放在末尾,index要偏移一个值
if (j == index) index++;
}
//index表示 单调递增的长度(恰好在上面的操作中+1了),剩下的n -index就是要翻转的字符,即翻转的操作数
return n - index;
}
public int subarraysDivByK(int[] A, int K) {
int sum = 0, ans = 0, n = A.length;
int[] map = new int[K];
map[0] = 1;
for (int i = 1; i <= n; i++) {
sum += A[i - 1];
int key = (sum % K + K) % K;
ans += map[key];
map[key]++;
}
return ans;
}
public int subarraysDivByK(int[] A, int K) {
int n = A.length;
int[] pre = new int[n + 1];
for (int i = 0; i < n; i++) {
pre[i + 1] = pre[i] + A[i];
}
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
if ((pre[i] - pre[j]) % K == 0) ans++;
}
}
return ans;
}
public boolean carPooling(int[][] trips, int capacity) {
int n = 1010;
int[] d = new int[n];
for (int[] t : trips) {
d[t[1]] += t[0];
d[t[2]] -= t[0];
}
for (int i = 0; i < n - 1; i++) {
if (d[i] > capacity) return false;
d[i + 1] += d[i];
}
return true;
}
public int[] corpFlightBookings(int[][] bookings, int n) {
//差分数组
int[] d = new int[n + 1];
for (int[] b : bookings) {
int l = b[0] - 1, r = b[1] - 1, v = b[2];
d[l] += v;
d[r + 1] -= v;
}
int[] res = new int[n];
res[0] = d[0];
for (int i = 1; i < n; i++) res[i] = res[i - 1] + d[i];
return res;
}
public int[] xorQueries(int[] arr, int[][] queries) {
int n = arr.length;
int[] pre = new int[n + 1];
for (int i = 0; i < n; i++) pre[i + 1] = pre[i] ^ arr[i];
int m = queries.length;
int[] ans = new int[m];
for (int i = 0; i < m; i++) {
int[] q = queries[i];
ans[i] = pre[q[1] + 1] ^ pre[q[0]];
}
return ans;
}
public int[] runningSum(int[] nums) {
int n = nums.length;
int[] pre = new int[n];
pre[0] = nums[0];
for(int i =1;i<n;i++) pre[i] =pre[i-1]+nums[i];
return pre;
}
public int numOfSubarrays(int[] arr) {
int MOD = (int) 1e9 + 7;
long ans = 0;
int n = arr.length;
int[] pre = new int[n + 1];
long odd = 0, even = 0;//奇数的个数,偶数的个数
for (int i = 0; i < n; i++) {
pre[i + 1] = pre[i] + arr[i];
if ((pre[i + 1] % 2) == 1) odd++;
else even++;
}
ans = (odd * even) % MOD + odd;
return (int) ans % MOD;
}
public int numOfSubarrays(int[] arr) {
final int MOD = (int) 1e9 + 7;
int odd = 0, even = 1;
int res = 0;
int sum = 0;
int length = arr.length;
for (int i = 0; i < length; i++) {
sum += arr[i];
res = (res + (sum % 2 == 0 ? odd : even)) % MOD;
if (sum % 2 == 0) {
even++;
} else {
odd++;
}
}
return res;
}
public int[] getSumAbsoluteDifferences(int[] nums) {
int n = nums.length;
int[] pre = new int[n];
int t = 0;
for (int i = 0; i < n; i++) {
t += nums[i];
pre[i] = t;
}
int[] res = new int[n];
for (int i = 0; i < n; i++) {
int left = (i + 1) * nums[i] - pre[i];
int right = pre[n - 1] - pre[i] - (n - i - 1) * nums[i];
res[i] = left + right;
}
return res;
}
public int kthLargestValue(int[][] matrix, int k) {
int R = matrix.length, C = matrix[0].length;
int[][] arr = new int[R + 1][C + 1];
PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> o2 - o1);
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
arr[i][j] = arr[i - 1][j - 1] ^ arr[i - 1][j] ^ arr[i][j - 1] ^ matrix[i - 1][j - 1];
pq.offer(arr[i][j]);
}
}
while (k-- > 1) pq.poll();
return pq.peek();
}
public int kthLargestValue(int[][] matrix, int k) {
int m = matrix.length, n = matrix[0].length;
int[][] pre = new int[m + 1][n + 1];
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
pre[i][j] = pre[i - 1][j] ^ pre[i][j - 1] ^ pre[i - 1][j - 1] ^ matrix[i - 1][j - 1];
list.add(pre[i][j]);
}
}
Collections.sort(list, (a, b) -> b - a);
return list.get(k - 1);
}
public int kthLargestValue(int[][] matrix, int k) {
int m = matrix.length, n = matrix[0].length;
int[][] pre = new int[m + 1][n + 1];
List<Integer> results = new ArrayList<Integer>();
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
pre[i][j] = pre[i - 1][j] ^ pre[i][j - 1] ^ pre[i - 1][j - 1] ^ matrix[i - 1][j - 1];
results.add(pre[i][j]);
}
}
nthElement(results, 0, k - 1, results.size() - 1);
return results.get(k - 1);
}
public void nthElement(List<Integer> results, int left, int kth, int right) {
if (left == right) {
return;
}
int pivot = (int) (left + Math.random() * (right - left + 1));
swap(results, pivot, right);
// 三路划分(three-way partition)
int sepl = left - 1, sepr = left - 1;
for (int i = left; i <= right; i++) {
if (results.get(i) > results.get(right)) {
swap(results, ++sepr, i);
swap(results, ++sepl, sepr);
} else if (results.get(i) == results.get(right)) {
swap(results, ++sepr, i);
}
}
if (sepl < left + kth && left + kth <= sepr) {
return;
} else if (left + kth <= sepl) {
nthElement(results, left, kth, sepl);
} else {
nthElement(results, sepr + 1, kth - (sepr - left + 1), right);
}
}
public void swap(List<Integer> results, int index1, int index2) {
int temp = results.get(index1);
results.set(index1, results.get(index2));
results.set(index2, temp);
}
public long[] getDistances(int[] arr) {
// k: arr[i] v: i
Map<Integer, List<Integer>> map = new HashMap<>();
for (int i = 0; i < arr.length; i++) {
List<Integer> list = map.getOrDefault(arr[i], new ArrayList<>());
list.add(i);
map.put(arr[i], list);
}
long[] intervals = new long[arr.length];
for (List<Integer> ls : map.values()) {
long sum = 0;
for (int index : ls) sum += index - ls.get(0);
intervals[ls.get(0)] = sum;
for (int i = 1; i < ls.size(); i++) {
sum += (2 * i - ls.size()) * (ls.get(i) - ls.get(i - 1));
intervals[ls.get(i)] = sum;
}
}
return intervals;
}
public long[] getDistances(int[] arr) {
// k: arr[i] v: i
Map<Integer, List<Integer>> map = new HashMap<>();
for (int i = 0; i < arr.length; i++) {
List<Integer> list = map.getOrDefault(arr[i], new ArrayList<>());
list.add(i);
map.put(arr[i], list);
}
long[] intervals = new long[arr.length];
for (Map.Entry<Integer, List<Integer>> e : map.entrySet()) {
List<Integer> ls = e.getValue();
int m = ls.size();
long[] left = new long[m];
long[] right = new long[m];
for (int i = 1, j = m - 2; i < m; i++, j--) {
left[i] = left[i - 1] + i * (ls.get(i) - ls.get(i - 1));
right[j] = right[j + 1] + (m - j - 1) * (ls.get(j + 1) - ls.get(j));
}
for (int i = 0; i < m; i++) {
intervals[ls.get(i)] = left[i] + right[i];
}
}
return intervals;
}
public long numberOfWays(String s) {
int n = s.length();
long[] one = new long[n + 1];//记录'1'的数量,剩下的便是'0'的数量
for (int i = 1; i <= n; i++) {
one[i] = one[i - 1] + (s.charAt(i - 1) == '1' ? 1 : 0);
}
long res = 0;
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
if (c == '0') {
//形成101这样的结构
long a = one[i + 1], b = one[n] - one[i + 1];
res += a * b;
} else {
//形成010这样的结构
long a = i + 1 - one[i + 1], b = n - i - (one[n] - one[i]);
res += a * b;
}
}
return res;
}
public long numberOfWays(String s) {
long tot_zero = 0, cur_zero = 0;//统计所有'0'的数量 当前所有'0'的数量
int n = s.length();
for (int i = 0; i < n; i++) {
if (s.charAt(i) == '0') tot_zero++;
}
long res = 0;
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
if (c == '1') {//010
res += cur_zero * (tot_zero - cur_zero);
} else {//101
long cur_one = i - cur_zero;
// n - 所有的0 剩下所有的1 再减去当前的1 得到剩下的1
res += cur_one * (n - tot_zero - cur_one);
cur_zero++;
}
}
return res;
}
//注意使用long类型
public int minimumAverageDifference(int[] nums) {
int n = nums.length;
long[] pre = new long[n + 1];
for (int i = 0; i < n; i++) pre[i + 1] = pre[i] + nums[i];
long minn = 100010;
int index = 0;
for (int i = 0; i < n; i++) {
long front = (int) (pre[i + 1] / (i + 1));
long back = i == n - 1 ? 0 : (long) ((pre[n] - pre[i + 1]) / (n - i - 1));
long diff = Math.abs(front - back);
if (diff < minn) {
minn = diff;
index = i;
}
}
return index;
}