贡献法
机器人碰撞可以视为机器人互相穿过对方,最后,如何计算两两机器人之间的距离之和。
方法一:贡献法: 累积计算两个点之间的线段距离和
class Solution {public : const int MOD=1e9 +7 ; int sumDistance (vector<int >& nums, string s, int d) { int n=nums.size (); for (int i=0 ;i<n;i++){ if (s[i]=='L' ){ nums[i]-=d; }else { nums[i]+=d; } } sort (nums.begin (),nums.end ()); long long line=0 ; long long ans=0 ; for (int i=1 ;i<n;i++){ line+=n-i; ans=(ans+(line%MOD)*((long long )nums[i]-nums[i-1 ]))%MOD; line-=i; } return ans; } };
方法二:前缀和
第i个机器人与其左侧i-1个机器人距离之和:
$$
(a[i]-a[0])+(a[i]-a[1])+…+(a[i]-a[i-1])=i*a[i]-(a[0]+a[1]+…+a[i-1])
$$
class Solution {public : const int MOD=1e9 +7 ; int sumDistance (vector<int >& nums, string s, int d) { int n=nums.size (); vector<long long > a (n) ; for (int i=0 ;i<n;i++){ if (s[i]=='L' ){ a[i]=(long long )nums[i]-d; }else { a[i]=(long long )nums[i]+d; } } sort (a.begin (),a.end ()); long long ans=0 ,sum=0 ; for (int i=0 ;i<n;i++){ ans=(ans+i*a[i]-sum)%MOD; sum+=a[i]; } return ans; } };
方法三:贡献法(累积每个数字的贡献)
class Solution {public : const int MOD=1e9 +7 ; int sumDistance (vector<int >& nums, string s, int d) { int n=nums.size (); vector<long long > result (n) ; for (int i=0 ;i<n;i++){ if (s[i]=='L' ){ result[i]=(long long )nums[i]-d; }else { result[i]=(long long )nums[i]+d; } } sort (result.begin (),result.end ()); long long sum=accumulate (result.begin (),result.end (),0LL ); long long ans=0 ; for (int i=0 ;i<n;i++){ sum-=result[i]; ans=(ans+(sum-(n-1 -i)*(long long )result[i]))%MOD; } return ans; } };
方法一:枚举
题目规定sarr[i+1] - sarr[i] > 1
为不平衡数字。
枚举子数组,i作为左侧边界,同时不断扩展右侧边界。在扩展右侧边界时,记录不平衡数字变化情况。如果x-1
和x+1
都存在,添加x
使得不平衡数字减1。都不存在时,将使得当前数字与某一个数字构成不平衡数字。
class Solution {public : int sumImbalanceNumbers (vector<int >& nums) { int n=nums.size (); bool vis[n+2 ]; int ans=0 ; for (int i=0 ;i<n;i++){ memset (vis,0 ,sizeof (vis)); vis[nums[i]]=true ; int cnt=0 ; for (int j=i+1 ;j<n;j++){ int x=nums[j]; if (!vis[x]){ cnt+=1 -vis[x-1 ]-vis[x+1 ]; vis[x]=true ; } ans+=cnt; } } return ans; } };
方法二:贡献法
统计每个数字对不平衡数字的贡献数,x=nums[i]
,子数组中如果存在x-1
,则x
肯定无法成为不平衡数字。同时为了避免重复统计,子数组中可以存在x+1
。这考虑的是x
作为不平衡数字的较大数字的贡献度。子数组左边可以包含x
。同时,每个数字可能作为子数组的最小值,这种情况排序后的子数组分别有n,n-1,..,1
个。
class Solution { public : int sumImbalanceNumbers (vector<int > &nums) { int n = nums.size (); vector<int > right (n) ; vector<int > idx (n + 1 , n) ; for (int i = n - 1 ; i >= 0 ; i--) { int x = nums[i]; right[i] = min (idx[x], idx[x - 1 ]); idx[x] = i; } int ans = 0 ; idx.assign (n + 1 , -1 ); for (int i = 0 ; i < n; i++) { int x = nums[i]; int l = idx[x - 1 ], r = right[i]; ans += (i - l) * (r - i); idx[x] = i; } return ans - n * (n + 1 ) / 2 ; } };
$$
a,b,c,d,e\
d作为最大值:a22+b*2 1+c 2^0+d=s+d\
e作为最大值:a23+b*2 3+c 21+d*2 0+e=2*s+d+e
$$
class Solution {public : const int MOD=1e9 +7 ; int sumOfPower (vector<int >& nums) { sort (nums.begin (),nums.end ()); int s=0 ; int ans=0 ; for (long long num:nums){ ans=(((s+num)*num%MOD)*num+ans)%MOD; s=(s*2 +num)%MOD; } return ans; } };
不限制右侧可以等于的情况产生重复计算
class Solution { public : const int MOD = 1e9 + 7 ; int sumSubarrayMins (vector<int > &arr) { int n = arr.size (); stack<int > stk; vector<int > right (n, n) ; for (int i = n - 1 ; i >= 0 ; i--) { while (!stk.empty () && arr[stk.top ()] > arr[i]) { stk.pop (); } if (!stk.empty ()) { right[i] = stk.top (); } stk.push (i); } int ans = 0 ; while (!stk.empty ()) stk.pop (); for (long i = 0 ; i < n; i++) { while (!stk.empty () && arr[stk.top ()] >= arr[i]) { stk.pop (); } int l = stk.empty () ? -1 : stk.top (); int r = right[i]; ans = (ans + (i - l) * (r - i) * arr[i]) % MOD; stk.push (i); } return ans; } };
先去模再比较最大值会出现问题,11%10=1,9%10=9,但实际是11更大。
class Solution {public : const int MOD=1e9 +7 ; int maxSumMinProduct (vector<int >& nums) { int n=nums.size (); vector<int > right (n,n) ; stack<int > stk; for (int i=n-1 ;i>=0 ;i--){ while (!stk.empty ()&&nums[stk.top ()]>=nums[i]) stk.pop (); if (!stk.empty ()) right[i]=stk.top (); stk.push (i); } while (!stk.empty ()) stk.pop (); vector<long long > sum (n+1 ) ; sum[0 ]=0 ; for (int i=1 ;i<=n;i++){ sum[i]=sum[i-1 ]+nums[i-1 ]; } long long ans=0 ; for (int i=0 ;i<n;i++){ while (!stk.empty ()&&nums[stk.top ()]>=nums[i]) stk.pop (); int l=stk.empty ()?-1 :stk.top (); int r=right[i]; stk.push (i); ans=max (ans,nums[i]*(sum[r]-sum[l+1 ])); } return ans%MOD; } };
单调栈+贡献法,分别计算每个数作为最大值、最小值的贡献
class Solution {public : long long subArrayRanges (vector<int >& nums) { int n=nums.size (); vector<int > minRight (n,n) ,minLeft (n,-1 ) ; vector<int > maxRight (n,n) ,maxLeft (n,-1 ) ; stack<int > minStk,maxStk; for (int i=n-1 ;i>=0 ;i--){ while (!minStk.empty ()&&nums[minStk.top ()]>nums[i]) minStk.pop (); if (!minStk.empty ()) minRight[i]=minStk.top (); minStk.push (i); while (!maxStk.empty ()&&nums[maxStk.top ()]<nums[i]) maxStk.pop (); if (!maxStk.empty ()) maxRight[i]=maxStk.top (); maxStk.push (i); } minStk=stack <int >(); maxStk=stack <int >(); for (int i=0 ;i<n;i++){ while (!minStk.empty ()&&nums[minStk.top ()]>=nums[i]) minStk.pop (); if (!minStk.empty ()) minLeft[i]=minStk.top (); minStk.push (i); while (!maxStk.empty ()&&nums[maxStk.top ()]<=nums[i]) maxStk.pop (); if (!maxStk.empty ()) maxLeft[i]=maxStk.top (); maxStk.push (i); } long long ans=0 ; for (long long i=0 ;i<n;i++){ long long minP=(i-minLeft[i])*(minRight[i]-i)*nums[i]; long long maxP=(i-maxLeft[i])*(maxRight[i]-i)*nums[i]; ans=ans-minP+maxP; } return ans; } };
求的是子数组的和*最小值,然后所有子数组的情况再进行求和
$$
\begin{aligned}
&sum是strength的前缀和,ss是sum的前缀和\
&对于[L+1,R-1]范围内,和为sum[R]-sum[L-1]\
&[L+1,R-1]范围内的所有子数组:\ \ \
&\sum_{p=L+1}{i}\sum_{q=i} {R-1}(sum[q+1]-sum[p])\
&=(i-L)\sum_{q=i}{R-1}sum[q+1]-(R-i)\sum_{p=L+1} {i}sum[p]\
&=(i-L)[sum[i+1]…sum[R]]-(R-i)[sum[L+1]…sum[i]]\
&=(i-L)(ss[R+1]-ss[i+1])-(R-i)(ss[i+1]-ss[L+1])
\end{aligned}
$$
class Solution { public : const int MOD = 1e9 + 7 ; int totalStrength (vector<int > &strength) { int n = strength.size (); vector<int > right (n, n) ; stack<int > stk; for (int i = n - 1 ; i >= 0 ; i--) { while (!stk.empty () && strength[stk.top ()] > strength[i]) stk.pop (); if (!stk.empty ()) right[i] = stk.top (); stk.push (i); } vector<int > sum (n + 1 ) ; for (int i = 1 ; i <= n; i++) { sum[i] = (sum[i - 1 ] + strength[i - 1 ]) % MOD; } vector<long long > ss (n + 2 ) ; ss[0 ] = sum[0 ]; for (int i = 1 ; i <= n + 1 ; i++) { ss[i] = (ss[i - 1 ] + sum[i - 1 ]) % MOD; } stk = stack <int >(); int ans = 0 ; for (int i = 0 ; i < n; i++) { while (!stk.empty () && strength[stk.top ()] >= strength[i]) stk.pop (); int l = stk.empty () ? -1 : stk.top (); int r = right[i]; stk.push (i); long long tmp = (i - l) * (ss[r + 1 ] - ss[i + 1 ]) - (r - i) * (ss[i + 1 ] - ss[l + 1 ]); ans = (ans + (tmp % MOD) * strength[i]) % MOD; } return (ans+MOD)%MOD; } };
分清每次枚举数字时的单个状态变化,以及需要求的答案总状态
$$
\begin{aligned}
a,b,c,d,e\
以d作为最大值的贡献:s&=22*|d-a|+2 1*|d-b|+2^0*|d-c|\
以e作为最大值的贡献:s’&=23*|e-a|+2 2*|e-b|+21*|e-c|+2 0*|e-d|\
&=2*(22*|d-a|+2 1*|d-b|+2^0*|d-c|)\
&\quad +23*|e-d|+2 2*|e-d|+21*|e-d|+2 0*|e-d|\
&=2s+(23+2 2+21+2 0) |e-d|
\end{aligned}
$$
class Solution {public : const int MOD=1e9 +7 ; int sumSubseqWidths (vector<int >& nums) { int n=nums.size (); sort (nums.begin (),nums.end ()); long long total=0 ,s=0 ; int ans=0 ; int pre=nums[0 ]; for (int num:nums){ s=(2 *s+total*(num-pre))%MOD; ans=(ans+s)%MOD; total=(2 *total+1 )%MOD; pre=num; } return ans; } };
方法二:考虑多少个e减去前面数的和
class Solution { public int sumSubseqWidths (int [] nums) { Arrays.sort(nums); int MOD = (int )1e9 +7 ; long res = 0 ; long sum = 0 , total = 0 ; for (long num : nums){ res = (res + num % MOD * total - sum) % MOD; sum = (sum * 2 + num) % MOD; total = (total * 2 + 1 ) % MOD; } return (int )res; } }