双指针问题
1.双指针暴力超时,优化方案
当数组中只存在1和2的值的时候我们可以考虑用二分去优化,我们可以找到数组中最后一个1的值,前面都是1和2的话
我们可以通过最后一个1去灵活地凑出 第一个数到最后一个1的数的和中间的任意一个值(划重点)
当然我们要尽可能凑出来最大的值,如果最后一个1后面的2比第一个1前面的2多,我们用后缀和表示 总和
反之,我们用前缀和表示出我们能表示出的总和的最大值(代码用t来表示)
在此之前,我们需要用一个set来记录每一个1出现的位置,即a数组的下标,每次出现就插入;如果set为空,那么表示
全都为偶数,所以要求我们表示v必须也是偶数才能yes;我们根据set数组也能找到第一个1之前和最后一个1之后的2的个数
用我们的数组总和减去 我们找到的最小的2个数那个区间,得到的就是我们能表示出来的最大的数字t;
在后面的表示中,如果小于我们能表示的数,表示我们能凑出来,为yes;如果大于,我们再用剩余那段2去表示,能表示的
前提就是v和t要是同奇同偶,且v - t 差值要小于我们这段2的个数*2 ,否则表示失败
如果替换的话,就用set去动态维护即可,每次根据v的数字去决定插入还是消除
set 是集合 可以自动去重,自动排序

1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N = 3e5 + 10; 5 6 int t,m,n; 7 int a[N],b[N],c[N],ans[N]; 8 9 int main() 10 { 11 scanf("%d", &t); 12 while(t --) 13 { 14 cin >> n >> m; 15 set<int> se; 16 17 int s = 0; 18 for (int i = 1; i <= n; i ++ ) 19 { 20 scanf("%d", &a[i]); 21 s += a[i]; //s用来记录总和 22 if(a[i] == 1) se.insert(i); //把每一个数字1 的位置丢到set中 23 } 24 25 while (m -- ){ 26 int op,x,v; 27 scanf("%d", &op); 28 if(op == 1) 29 { 30 scanf("%d", &v); //读入要判断的数 31 if(se.empty()) puts(v % 2 == 0 ? "YES" : "NO"); //如果se为空,表示全都是2,那么v是偶数就可以凑出来 32 else 33 {//判断是第一个1出现之前的2少还是最后一个1出现之后的2少 34 int cnt = min(*se.begin() - 1,n - *se.rbegin()); 35 int t = s - cnt * 2; //表示除开较少2的那段的数字和 36 if(v <= t ||(v % 2 == t % 2 && (v - t) / 2 <= cnt)) puts("YES"); 37 //如果 要凑的那个值小于我能表示的值,为yes,如果大于,则用后面的或者前面那段较少的2去维护 38 // 所以这个前提是 要凑的数和我能表示的最大数同奇同偶,然后大的差值要小于我能凑出的2个数*2,否则凑不了 39 else puts("NO"); 40 } 41 } 42 else { 43 scanf("%d%d", &x, &v); 44 if(a[x] == 1) se.erase(x); //等于1 换掉之后要出set 45 if(v == 1) se.insert(x); //换的值为1 换之后要进set 46 s += v- a[x]; //维护s的值 47 a[x] = v; 48 } 49 } 50 } 51 return 0; 52 }
2.Problem - C - Codeforces 利用双指针来不断优化最小值,计算不操作,一次操作,两次操作的最小值(这里采取lower_bound)进行优化

1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define cy cout << "Yes" << endl 5 #define cn cout << "No" << endl 6 7 const int N = 2010,inf = 1e9; 8 const int mode = 1e9 + 7; 9 int n,k,m; 10 ll a[N],b[N * N]; 11 12 int main(){ 13 int _; 14 cin >> _; 15 while(_ -- ) 16 { 17 cin >> n >> k; 18 for (int i = 1; i <= n; ++ i) cin >> a[i]; 19 if(k >= 3) 20 { 21 cout << 0 << endl; 22 continue; 23 } 24 25 if(k == 1) 26 { 27 ll ans = 2e18; 28 for (int i = 1; i <= n; ++ i) ans = min(ans,a[i]); 29 for (int i = 1; i <= n; ++ i) 30 for (int j = i + 1; j <= n; ++ j) 31 ans = min(ans,abs(a[i] - a[j])); 32 33 cout << ans << endl; 34 continue; 35 } 36 37 ll ans = 2e18; 38 for (int i = 1; i <= n; ++ i) ans = min(ans,a[i]); 39 m = 0; 40 for (int i = 1; i <= n; ++ i) 41 for (int j = i + 1; j <= n; ++ j) 42 b[++m] = abs(a[i] - a[j]); //进行一次操作 43 44 sort(b + 1,b + m + 1); //排序找到一次操作后的最小值且后面与一次操作都不做比大小求最小值 45 46 ans = min(ans,b[1]); 47 48 for (int i = 1; i <= n; ++ i){ 49 int t = lower_bound(b + 1, b + m + 1,a[i]) - b; //进行一次运算后第一个大于等于a[i]的数 50 if(t <= m) ans = min(ans,b[t] - a[i]);/*进行一次操作或不进行操作的ans和进行两次后 51 (该为第一个大于等于a[i]的数减去a[i]差值)*/ 52 if(t > 1) ans = min(ans,a[i] - b[t - 1]);//该为第一个小于a[i]的数减去a[i]差值 53 } 54 cout << ans << endl; 55 } 56 return 0; 57 }
3.Problem - 1669F - Codeforces 当前缀和大于后缀和时,指针移动,后缀和增加,否则前缀和增加,当相等的时候记录答案

1 #include<bits/stdc++.h> 2 using namespace std; 3 #define endl '\n' 4 #define ll long long 5 #define cy cout << "YES" << endl 6 #define cn cout << "NO" << endl 7 int _,n,m; 8 const int N = 2e5 + 10,inf = 1e9; 9 const int mod = 1e9 + 7; 10 int a[N]; 11 int sum1,sum2,ans; 12 13 int main() 14 { 15 cin >> _; 16 while(_ -- ){ 17 cin >> n; 18 for (int i = 1; i <= n; i ++ ) cin >> a[i]; 19 ans = sum1 = sum2 = 0; 20 int x = 1,y = n; 21 while(x <= y){ 22 sum1 += a[x]; 23 while(y > x && sum1 > sum2) sum2 += a[y],y --; 24 if(sum1 == sum2) ans = max(ans,x + n - y); //吃到相同糖果记录答案 25 x ++; 26 } 27 //cout << x << " " << y << " "; 28 cout << ans << endl; 29 } 30 return 0; 31 }