近期刷题总结

B. Swaps(思维)

两个序列,a序列是一个1 ~ 2n的奇数排列,b是一个1 ~ 2n的偶数排列,如何交换使得a的字典序小于b。求最小的交换次数。
思维题。两种写法,都是很棒的写法。和排序都有关,第一种没有直接排序,但是也有排序的思想。都是利用贪心,缩小了答案的范围。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
int a[N], b[N];
struct node {
    int val, pos;
} a1[N], b1[N];
signed main()
{
    /* ios::sync_with_stdio(0);
    cin.tie(0); */
    int t;
    cin >> t;
    a[0] = 1e9 + 7;
    while(t--) {
        int n;
        scanf("%lld", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &a[i]);
            a[i] = min(a[i], a[i - 1]);
        }
        for (int i = 1; i <= n; i++) scanf("%lld", &b[i]);
        int num = n;
        int ans = 1e9 + 7;
        for (int i = 1; i <= n; i++) {
            while(b[i] > a[num]) num--;
            ans = min(ans, num + i - 1);
        }
        cout << ans << endl;
    }
    return 0;
}

如果a [ i ] > b [ num ] 的话,那么b [ num ] 之前的数一定无法和 a [ i ] 之后的数进行匹配。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
int b[N];
struct node {
    int val, pos;
    bool operator < (const node &k) const {
        return val < k.val;
    }
} a[N];
signed main()
{
    /* ios::sync_with_stdio(0);
    cin.tie(0); */
    int t;
    cin >> t;
    while(t--) {
        int n;
        scanf("%lld", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &a[i].val);
            a[i].pos = i;
        }
        for (int i = 1; i <= n; i++) scanf("%lld", &b[i]);
        sort(a + 1, a + 1 + n);
        int ans = 1e9 + 7;
        int num = 1;
        for (int i = 1; i <= n; i++) {
            while(a[i].val > b[num]) num++;
            ans = min(ans, num - 1 + a[i].pos - 1);
        }
        cout << ans << endl;
    }
    return 0;
}

(E) Easy Scheduling

很多时候,尽量的的模拟题意,就能避开大多数wa。
这题就是一颗二叉树,每次最多选择p个节点进入准备,但是一开始只能选1个节点,然后是2,4,8个。但是不能超过p个。所以一个循环模拟就好了。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 500 + 5, mod = 998244353;
signed main()
{
    /* ios::sync_with_stdio(0);
    cin.tie(0); */
    int t;
    cin >> t;
    while(t--) {
        int h, p;
        scanf("%lld%lld", &h, &p);
        int sum = (long long)pow(2, h) - 1;
        // cout << sum << endl;
        int k = 1;
        int ans = 0;
        for (int i = 1; k <= p; k *= 2, i++) {
            sum -= k;
            ans = i;
            if (sum <= 0) {
                // ans = i;
                break;
            }
        }
        int a1 = ceill(sum * 1.0 / p);
        cout << a1 + ans << endl;
    }   
    return 0;
}

(D) Backspace(子序列+模拟)

输入字符是一个一个输入的,如果将其中一个字符替换成删除,那么就相当于原串删除两个字符。
这题倒过来验证t是否为s的子序列就好了,匹配得上,那就继续匹配,匹配不上,那就模拟删除操作,不知道dp怎么写。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
signed main()
{
    /* freopen("data5.in","r",stdin);
	freopen("data5.out","w",stdout); */
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T;
    cin >> T;
    while(T--) {
        string s, t;
        cin >> s >> t;
        int lens = s.length(), lent = t.length();
        int i = lens, j = lent;
        bool ck = 0;
        for (; i >= 0; ) {
            if (s[i] == t[j]) i--, j--;
            else i -= 2;
            if (j < 0) {
                ck = 1;
                break;
            }
            if (i < 0) break;
        }
        if (ck) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}

© Penalty(贪心或暴力dfs)

一道回溯的dfs,但是写了好久,搜索的功底太差了。
想法就是遇见一个?就分成两支往下搜索。
有很多个细节没有注意到:
1.dfs和循环嵌套,这样写大大增加了复杂度,应该是阶乘级别的了。
2.两个分支往下的时候,要判断step是奇数还是偶数,奇数才能给a+1,偶数才能给b+1。
3.遇到能停止发球的情况,不能马上return,因为可能还有更优解。
4.形成分支往下搜索的时候,这一次的状态相当于已经确定了,要立即进行判断是否能停止发球,不能等进入下一个状态再进行判断。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
int ans;
string s;
void dfs(int step, int a, int b)
{
    if (step >= 10) {
        ans = min(ans, step);
        return;
    }
    if (s[step] == '1') {
        if (step & 1) a++;
        else b++;
    }

    int sa = (10 - step) / 2, sb = (10 - step) / 2;
    if (step & 1) sb++;
    if (a + sa < b || b + sb < a) {
        ans = min(ans, step);
        // printf("st = %lld, a = %lld, b = %lld, ans = %lld\n", step, a, b, ans);
        // return;
    }
    if (s[step] == '?') {
        if (step & 1) {
            int sa = (10 - step) / 2, sb = (10 - step) / 2;
            if (step & 1) sb++;
            if (a + sa + 1 < b || b + sb < a + 1) {
                ans = min(ans, step);
            }
            dfs(step + 1, a + 1, b);
        }

        if ((step & 1) == 0) {
            dfs(step + 1, a, b + 1);
            int sa = (10 - step) / 2, sb = (10 - step) / 2;
            if (step & 1) sb++;
            if (a + sa < b + 1 || b + sb + 1 < a) {
                ans = min(ans, step);
            }
        }

        dfs(step + 1, a, b);
    }
    else dfs(step + 1, a, b);
}
signed main()
{
    /* ios::sync_with_stdio(0);
    cin.tie(0); */
    int T;
    cin >> T;
    while(T--) {
        cin >> s;
        s = " " + s;
        ans = 1e9 + 7;
        dfs(1, 0, 0);
        cout << ans << endl;
    }
    return 0;
}

其实还有复杂度更低的O(N)的写法,也很好理解,贪心的将问号单一转为其中一方得分就好了,试两次,取min。

#include<bits/stdc++.h>
using namespace std;
// 9 19 29 39
#define MAX 10000
int main(){
    int T;
    cin >> T;
    while(T--){
        string s;
        cin >> s;
        int A = 10;
        int B = 10;
        int scoreA = 0;
        int scoreB = 0;
        for(int i = 0; s[i] != '\0'; i++){
            if(i % 2 == 0){
                if(s[i] == '1' || s[i] == '?')
                    scoreA++;
            }
            if(i % 2 == 1){
                if(s[i] == '1')
                    scoreB++;
            }
            if(i % 2 == 0){
                if(scoreA + 5 - (i+2)/2 < scoreB || scoreB + 5 - i/2 < scoreA){
                    A = i+1;
                    break;
                }
            }
            else{
                if(scoreA + 5 - (i+1)/2 < scoreB || scoreB + 5 - (i+1)/2 < scoreA){
                    A = i+1;
                    break;
                }
            }
        }
        scoreA = 0;
        scoreB = 0;
        for(int i = 0; s[i] != '\0'; i++){
            if(i % 2 == 0){
                if(s[i] == '1')
                    scoreA++;
            }
            if(i % 2 == 1){
                if(s[i] == '1' || s[i] == '?')
                    scoreB++;
            }
            if(i % 2 == 0){
                if(scoreA + 5 - (i+2)/2 < scoreB || scoreB + 5 - i/2 < scoreA){
                    B = i+1;
                    break;
                }
            }
            else{
                if(scoreA + 5 - (i+1)/2 < scoreB || scoreB + 5 - (i+1)/2 < scoreA){
                    B = i+1;
                    break;
                }
            }
        }
        cout << min(A,B) << endl;
    }

}

J. Jeopardy of Dropped Balls(并查集)

思路是正确的,用数据结构优化连续的2,来达到提高下落的速度。
但是为什么暴力都不TLE,而我不暴力却一直TLE,想不出来出了什么bug。
贴一下加了并查集的代码。
这题时限两秒,其实暴力模拟是能稳过的。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e3 + 5, mod = 1000000007;
int mp[N][N];
int f[N][N];
int getf(int x,int y) {
	if (f[x][y] == x) return x;
    return f[x][y] = getf(f[x][y], y);
}
signed main()
{
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            scanf("%lld", &mp[i][j]);
            if (mp[i][j] == 2) f[i][j] = i + 1;
            else f[i][j] = i;
        }
    }
    while (k--) {
        int i = 1, j;
        scanf("%lld", &j);
        while(mp[i][j]) {
            if (mp[i][j] == 1) {
                f[i][j] = f[i + 1][j];
                mp[i][j] = 2;
                j = j + 1;
            }
            else if (mp[i][j] == 3) {
                f[i][j] = f[i + 1][j];
                mp[i][j] = 2;
                j = j - 1;
            }
            else i = getf(i, j);
        }
        printf("%lld ", j);
    }
    return 0;
}

E. Air Conditioners(dp)

每个格子的温度其实都是由相邻格子提供;
如果一个点不能更新它的下一个点,那么,它后面的点都不可能会被它更新。
首先把空调的值都放好在对应的pos里。
试想,先考虑空调只能影响其右边的格子,从左往右遍历一遍,不断地取min;
再考虑空调对左边的格子的影响,从右往左遍历一遍。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 5, INF = 0x3f3f3f3f;
int a[N], pos[N];
int f[N];
signed main()
{
    int q;
    cin >> q;
    while (q--) {
        int n, k;
        scanf("%lld%lld", &n, &k);
        for (int i = 1; i <= k; i++) scanf("%lld", &pos[i]);
        for (int i = 0; i <= n + 1; i++) f[i] = INF;
        for (int i = 1; i <= k; i++) {
            int x;
            scanf("%lld", &x);
            f[pos[i]] = x;
        }
        for (int i = 1; i <= n; i++) f[i] = min(f[i], f[i - 1] + 1);
        for (int i = n; i >= 1; i--) f[i] = min(f[i], f[i + 1] + 1);
        for (int i = 1; i <= n; i++) printf("%lld ", f[i]);
        printf("\n");
    }
    return 0;
}

© Moamen and XOR(分类讨论+位运算)

上上个月没做出的题,其实当时的思路是正确的,只是考虑的情况太多了,其实没那么多的。
题要求&的结果大于等于xor,要大于,只有1 > 0的情况,那么&运算结果是1,xor运算结果是0,所以要求该二进制位上,所有数字均为1,且一共的n个数是偶数。
那么这题就开始奇偶讨论。
偶数:
1 > 0,第 s 位上全为 1,s 位以前的位满足 1 == 1 或者是 0 == 0;s 位以后的数任意填,(重做的时候我忽略了后面全为0的情况,导致wa)。
奇数:
不可能存在 1 > 0 的情况,只有1 == 1和 0 == 0。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 5, mod = 1e9 + 7;
int a[N], pos[N];
int f[N], inv[N];
int qpow(int x, int n)
{
    int ret = 1;
    while(n) {
        if (n & 1) ret = ret * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return ret;
}
int C(int n, int m)
{
    if (m == 0 || n == m) return 1;
    return f[n] * inv[m] % mod * inv[n - m] % mod;
}
signed main()
{
    int q;
    cin >> q;
    f[0] = 1;
    for (int i = 1; i <= 200005; i++) f[i] = f[i - 1] * i % mod;
    for (int i = 1; i <= 200005; i++) inv[i] = qpow(f[i], mod - 2);
    while (q--) {
        int n, k;
        cin >> n >> k;
        int ans = 0;
        if (n & 1) {
            for (int s = 0; s <= k; s++) {
                ans = (ans + C(k, s) * qpow(qpow(2, n - 1), s) % mod) % mod;
            }
        }
        else {
            for (int p = k; p >= 1; p--) {
                ans = (ans + qpow(qpow(2, p - 1), n) * qpow(qpow(2, n - 1) - 1, k - p) % mod) % mod;
            }
            ans = (ans + qpow(qpow(2, n - 1) - 1, k)) % mod;
        }
        cout << ans << endl;
    }
    return 0;
}

© Make Them Equal 分类讨论+细节

很容易考虑不全的一道题。
在字符串下标中选一个数x,不能被x整除的下标就可以改为目标字符,求一个最小的修改次数。
一开始想,如果是选择下标n,那么最多只需要修改两次就好了, 1 ~ n - 1 一次,如果第 n 位不是目标字符,那么就再修改一次。
结果就wa了。于是又想选择 n - 1 ?结果又是 wa 了好多回。
最后意识到,从 x 从 n / 2 + 1 开始,所有的下标就已经不能被 x 整除了,所以,从 n / 2 + 1 ~ n,只要有一个下标为目标字符,就可以直接输出这个下标,修改次数为1。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e3 + 5, mod = 1e9 + 7;
signed main()
{
    int T;
    cin >> T;
    while(T--) {
        int n;
        char x;
        string s;
        cin >> n >> x >> s;
        if (n == 1) {   
            if (s[0] == x) cout << 0;
            else {
                cout << 1 << endl;
                cout << 1;
            }
            cout << endl;
            continue;
        }
        bool ck = 1;
        for (int i = 0; i < n; i++) {
            if (s[i] != x) {
                ck = 0;
                break;
            } 
        }
        if (ck) {
            cout << 0 << endl;
            continue;
        }
        ck = 0;
        for (int i = n / 2; i < n; i++) {
            if (s[i] == x) {
                cout << 1 << endl;
                cout << i + 1 << endl;
                ck = 1;
                break;
            }
        }
        if (!ck) {
            cout << 2 << endl;
            cout << n - 1 << " " << n << endl;
        }
    }
    return 0;
}
// ftp://192.168.101.1/

(F) Array Stabilization (AND version)分类讨论+思维+位运算

洛谷的题解质量真的很高,把思路讲的明明白白的。
一个 0 1 字符串,一次操作是循环右移 d 位后,与原串进行 & 操作,获得一个新串。求最小的操作次数。
每一个位置上的数字都有她自己的固定路线,她只会走到固定的那些个位子上去,不会被不相干的位子上的数字所影响。
所以分离出这些位子,分成 gcd(n , d) 组。如果分离出的一组数全为 1 ,那么答案就是-1。其他的情况,容易看出答案是最长的连续 1 的长度。
下面的代码之所以分离两次,是因为开头结尾要是可能会连续,不能漏解。

#include <bits/stdc++.h>
#define int long long
namespace mystd {
    inline int read() {
        int w = 1, q = 0;
        char ch = ' ';
        while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
        if (ch == '-') w = -1, ch = getchar();
        while (ch >= '0' && ch <= '9') q = q * 10 + ch - '0', ch = getchar();
        return w * q;
    }
    inline void write(int x) {
        if (x < 0) {
            x = ~(x - 1);
            putchar('-');
        }
        if (x > 9) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace std;
using namespace mystd;

const int maxn = 1e6 + 100;
int t, n, d, g, ans, s[maxn], tp[maxn << 1];

int gcd(int x, int y) {
    if (y == 0) return x;
    else return gcd(y, x % y);
}

signed main() {
    t = read();
    while (t--) {
        ans = 0;
        n = read();
        d = read();
        g = gcd(n, d);
        for (int i = 1; i <= n; i++) {
            s[i] = read();
        }
        for (int i = 1; i <= g; i++) {
            int c = 0;
            for (int j = 1; j <= n / g; j++) tp[++c] = s[(i + d * j - 1) % n + 1]/* , cout <<(i + d * j - 1) % n + 1 << " " */;
            // cout << endl;
            for (int j = 1; j <= n / g; j++) tp[++c] = s[(i + d * j - 1) % n + 1];
            int tmp = 0, res = 0;
            for (int i = 1; i <= c; i++) {
                if (tp[i] == 1) tmp++;
                else tmp = 0;
                res = max(res, tmp);
            }
            ans = max(ans, res);
        }
        write(ans == n / g * 2 ? -1 : ans);
        puts("");
    }
    return 0;
}
//tql

还有一种解法,其实核心思想是一样的,但是实现的形式更加简单,直接用队列存储了0的下标。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 998244353;
int a[N];
signed main()
{
    int T;
    cin >> T;
    while (T--) {
        int n, d;
        cin >> n >> d;
        queue<int> q;
        for (int i = 0; i <= n - 1; i++) {
            scanf("%lld", &a[i]);
            if (!a[i]) {
                q.push(i);
            }
        }
        int ans = 0;
        while(!q.empty()) {
            int len = q.size();
            for (int i = 1; i <= len; i++) {
                int x = q.front();
                q.pop();
                int nxt = (x + n - d) % n;
                if (a[nxt]) {
                    a[nxt] = 0;
                    q.push(nxt);
                }
            }
            ans++;
        }
        bool ck = 0;
        for (int i = 0; i <= n - 1; i++) {
            if (a[i] == 1) {
                ck = 1;
                break;
            }
        }
        if (ck) cout << -1 << endl;
        else cout << ans -1 << endl;
    }
    return 0;
}

(E) Gardener and Tree(拓扑排序删点)

学会了用拓扑排序来达到删除叶子节点的目的。
题与普通的拓扑排序不同,普通的是单向边,这里是双向边。不过也大同小异,删除度为1的节点就好了。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6, INF = 0x3f3f3f3f;
vector<int> v[N];
int in[N];
struct node{
    int val, e;
};
queue <node> q;
signed main()
{
    /* freopen("data5.in","r",stdin);
	freopen("data5.out","w",stdout); */
    int T;
    cin >> T;
    while(T--) {
        int n, k;
        cin >> n >> k;
        if (n == 1) {
            cout << 0 << endl;
            continue;
        }
        int a, b;
        for (int i = 1; i <= n - 1; i++) {
            cin >> a >> b;
            in[a]++;
            in[b]++;
            v[a].push_back(b);
            v[b].push_back(a);
        }
        for(int i=1; i<=n; i++) {
            if(in[i] == 1) q.push(node{0, i});
        }
        vector<int> ans;
        while(!q.empty()) {
            int p = q.front().e, now = q.front().val;
            if (now == k) break;
            q.pop(); // 选一个入度为1的点,出队列
            ans.push_back(p);
            int len = v[p].size();
            for(int i = 0; i < len; i++) {
                int y = v[p][i];
                in[y]--;
                if(in[y]==1) q.push(node{now + 1, y});  
            }
        }
        cout << n - ans.size() << endl;
        for (int i = 1; i <= n; i++) {
            v[i].clear();
            in[i] = 0;
        }
        while(!q.empty()) q.pop();
        ans.clear();
    }
    return 0;
}

A. AquaMoon and Strange Sort(奇偶分类讨论)

冒泡排序,每个数字只能移动偶数次,判断最后是否可以有序。
只能移动偶数次,那奇数位置移动完还在奇数位置,偶数位置也是。
那么就用桶记录每个数字出现的奇数有几次,偶数位置有几次,再给数组排序后,一一比对就好了。
注意清空数组时候的范围。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6, INF = 0x3f3f3f3f;
int c[N], a[N];
int odd[N], even[N];
signed main()
{
    int T;
    cin >> T;
    while(T--) {
        int n;
        cin >> n;
        int ma = -1;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            c[i] = a[i];
            ma = max(ma, a[i]);
        }
        sort(c + 1, c + 1 + n);
        for (int i = 1; i <= n; i++) {
            if (i & 1) odd[c[i]]++;
            else even[c[i]]++;
        }
        bool ck = 0;
        for (int i = 1; i <= n; i++) {
            if ((i & 1) && odd[a[i]]) odd[a[i]]--;
            else if (!(i & 1) && even[a[i]]) even[a[i]]--;
            else {
                ck = 1;
                break;
            } 
        }
        if (ck) cout << "NO" << endl;
        else cout << "YES" << endl;
        for (int i = 1; i <= ma; i++) odd[i] = even[i] = 0;
    }
    return 0;
}

(D) Co-growing Sequence

代码不放了,一直wa3的原因是数组开小了。。。
但是有一个写法稍异的写法,小小的数组却Ac了就真的很奇怪。。

(B) Groups

给出了n个人的空闲时间,判断是否可以分配出两天,学生分成两拨,每波n/2个,安排一波去其中一天干活。
用二维的set来保存每天都有空的学生,只要有那么两天,集合内的学生数>=n/2,两天的并集==n,那么一定可以YES。接着就是暴力了。。能想出来感觉也挺不容易的,。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e3 + 5, INF = 0x3f3f3f3f;
int a[N][10];
set<int> s[6];
signed main()
{
    int tt;
    cin >> tt;
    while(tt--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= 5; j++) {
                scanf("%lld", &a[i][j]);
                if (a[i][j]) s[j].insert(i);
            }
        }
        bool ck = 0;
        for (int i = 1; i <= 5; i++) {
            for (int j = i + 1; j <= 5; j++) {
                if (s[i].size() >= n / 2 && s[j].size() >= n / 2) {
                    set<int> t;
                    for (auto k : s[i]) t.insert(k);
                    for (auto k : s[j]) t.insert(k);
                    if (t.size() == n) {
                        ck = 1;
                        break;
                    }
                }
            }
            if (ck) break;
        }
        if (ck) cout << "YES" << endl;
        else cout << "NO" << endl;
        for (int i = 1; i <= 5; i++) s[i].clear();
    }
    return 0;
}

这题看大佬用bitset的奇淫技巧。

#include<bits/stdc++.h>
using namespace std;
int t,n;
bitset<1005> a[6];
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=5;++i)a[i]=0;
		for(int i=1,x;i<=n;++i)
		for(int j=1;j<=5;++j)
		{scanf("%d",&x);a[j][i]=x;}
		bool flag=0;
		for(int i=1;i<=5;++i)for(int j=i+1;j<=5;++j)
		if(a[i].count()>=n/2&&a[j].count()>=n/2&&(a[i]|a[j]).count()>=n)flag=1;
		printf(flag?"YES\n":"NO\n");
	}
	return 0;
}

F. Interesting Function(数位问题)

10对答案贡献多了1,100是2,1000是3…
所以n每次除以10就能统计了。相当于n在统计有几个10,几个100…
例如199,有19个10,1个100。这样子计算,其中的100对10有1的贡献,对100也有1的贡献,就相当于100有2的贡献了。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, INF = 0x3f3f3f3f;
int f(int n)
{
    int ret = 0;
    while(n) {
        ret += n;
        n /= 10;
    }
    return ret;
}
signed main()
{
    int tt;
    cin >> tt;
    while(tt--) {
        int l, r;
        cin >> l >> r;
        cout << f(r) - f(l) << endl;
    }
    return 0;
}

(D) Training Session(集合+思维)

给定n个x与y。选3个出来,使得其中的x全不相同或者是y全不相同,求有多少种选法。
用总的选法减去不合理的选法。
如果当前选了x与y,要选出另外两个来让这个选择不合理,那么其中一个一定含有x,另一个含有y;这个x的选法就是cnt[x]-1,y的选法就是cnt[y]-1。
因为不可能有x与y同时相等,所以这样子是不重不漏的。

x y
x 
  y 这样的组合一定不合理。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, INF = 0x3f3f3f3f;
struct node{
    int x, y;
} a[N];
int t1[N], t2[N];
signed main()
{
    int tt;
    cin >> tt;
    while(tt--) {
        int n;
        cin >> n;
        int sum = n * (n - 1) * (n - 2) / 6;
        for (int i = 1; i <= n; i++) {
            scanf("%lld%lld", &a[i].x, &a[i].y);
            t1[a[i].x]++;
            t2[a[i].y]++;
        }
        for (int i = 1; i <= n; i++) {
            sum -= (t1[a[i].x] - 1) * (t2[a[i].y] - 1);
        }
        cout << sum << endl;
        for (int i = 1; i <= n; i++) t1[i] = t2[i] = 0;
    }
    return 0;
}

(A) Computer Game

只能往右,斜着右上,或者斜着右下走。能否走到终点。
上课写的,dfs写了好久。。我的dfs功底太差了。其实根本不用写搜索,只要有那么一堵墙(这一列全为1),那么就无法走通。
见识一下bitset的奇淫技巧。

#include <bits/stdc++.h>
using namespace std;
int main() {
string s, t;
int k, n;
cin >> k;
while (k--)
cin >> n >> s >> t,
cout << ((bitset<100>(s) & bitset<100>(t)).count() ? "NO\n" : "YES\n");
}

(B) AquaMoon and Stolen String(桶优化暴力)

有n(n为奇数)个字符串,选n-1个出来,配成(n-1)/2对,每一对中,两个字符串之前的任意字符可以随意互换,(同下标才能换)。多出来的那个字符串被删去了,现在请你找到他。
思路:开一个字符桶 f [ 26 ] [ N ],反正字符换来换去还是在那一列。
这题是交互题,好像都要加一个fflush(stdout);

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, INF = 0x3f3f3f3f;
int tong[30][N];
char s[N];
signed main()
{
    /* freopen("data5.in","r",stdin);
	freopen("data5.out","w",stdout); */
    int tt;
    cin >> tt;
    while(tt--) {
        int n, m;
        scanf("%lld%lld", &n, &m);
        for (int i = 1; i <= n; i++) {
            scanf("%s", s + 1);
            for (int j = 1; j <= m; j++) tong[s[j] - 'a'][j]++;
        }
        for (int i = 1; i <= n - 1; i++) {
            scanf("%s", s + 1);
            for (int j = 1; j <= m; j++) tong[s[j] - 'a'][j]--;
        }
        string ans;
        for (int i = 1; i <= m; i++)  {
            for (int j = 0; j <= 25; j++) {
                if (tong[j][i]) {
                    ans = ans + char(j + 'a');
                    break;
                }
            }
        }
        cout << ans << endl;
        fflush(stdout);
        for (int i = 0; i <= 25; i++)
            for (int j = 1; j <= m; j++) tong[i][j] = 0;
    }
    fflush(stdout);
    return 0;
}

© Strange Function(最小公倍数)

f(x)是最小的不能被x整除的数。给定n,求前n项f的和。
因为n非常大,一开始以为像矩阵快速幂求斐波那契一样,但是不会。。然后想到阶乘,如果一个数x能整除1 * 2 * 3,那么f(x)就是4,但是这样子其实是不正确的。就比如f(12)=5。
看到题解说最小公倍数,然后灵机一动就会了~
我的想法是,如果n能够整除lcm(1 ~ k),那么n以内就有n/lcm(1 ~ k)个数是lcm(1 ~ k)的倍数,令t = lcm(1 ~ k + 1),n/lcm(1 ~ k)- t 个数的f值就是k + 1。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 1e9 + 7;
int f[N];
int lcm(int a, int b)
{
    return a / __gcd(a, b) * b;
}
signed main()
{
    /* int l = 1;
    for (int i = 1; i <= 100; i++) {
        l = lcm(l, i);
        cout << l << " ";
        if (l > 10000000000000000) {
            cout << i << " ";
            break;
        }
    } */
    f[0] = 1;
    for (int i = 1; i <= 41; i++) f[i] = lcm(f[i - 1], i);
    int tt;
    cin >> tt;
    while(tt--) {
        int n;
        cin >> n;
        int ans = 0;
        int last = 0;
        for (int i = 41; i >= 1; i--) {
            int c = n / f[i];
            if (c == 0) continue;
            ans = (ans + (c - last) * (i + 1)) % mod;
            last = c;
        }
        cout << ans << endl;
    }
    return 0;
}

把last写成next也可以,能少做几次循环,不过41算是个常数了,改不改无所谓了。

(B) Pleasant Pairs(枚举+调和级数)

给定n个不同的数,求a[i]*a[j] == i + j有多少对。
思想:由这个式子可以得知,i + j一定是a[i]的倍数,所以可以枚举 i + j 是a[i]的多少倍,而 i + j最大是2 * n,所以枚举的过程就像是调和级数,调和级数的复杂度是logn。所以总体复杂度是nlogn。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 1e9 + 7;
int a[N];
signed main()
{
    int tt;
    cin >> tt;
    while(tt--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = a[i] - i; i + j <= 2 * n; j += a[i]) {
                if (j <= 0 || j > n || i >= j) continue;
                if (a[i] * a[j] == i + j) {
                    ans++;
                }
            } 
        }
        cout << ans << endl;
    }
    return 0;
}

牛客小白月赛39:

D-绝望

很普通的一个势能线段树,wa了半个下午的原因,是修改线段树数组的时候,线段树更新了,而原序列a[i]没有及时更新,以前做的题目一直在树上改就好了,这回的题原序列也要改一次,因为修改线段树的时候的一些判断,是依赖于原序列的值的。

G-冷静

q 次询问。每次询问给定 n 和 K,问 1 ~ n 中有多少数可以表示为大于等于 K 的质数的乘积(一个数可以乘多次)。
原问题几乎相当于求1 ~ n的区间内,有多少个数是>=k的。 但是有一点不同的是,这些数是1 ~ n最小素因子。 直接上普通平衡树Treap。 有一点要注意的是,k在平衡树内不存在的情况,比赛的时候就因为这个我一直wawawa…呜呜呜了

H-终别

每次操作可以使3个连续的数-1。还有一次特殊操作,直接让两个相邻的数归零,但是这个操作只能用一次。求让所有数归零的最小操作次数。
一开始我以为是道dp,还整了个递推式出来,看起来还很有道理的一个递推式,实际上漏解了。
我整了个f[i] = min({f[i - 1] + a[i], f[i - 2] + max(a[i], a[i - 1]), f[i - 3] + max({a[i], a[i - 1], a[i - 2]})});意思是当前的a[i]单干,a[i]与a[i-1]一起干,a[i]与a[i-1]与a[i-2】一起干,这样是不对的,因为a[i]单干不如和a[i+1]与a[i+2]一起干。
正解是贪心,从左到右,如果当前的a[i]不为0,那就干到他变成0为止。
这样子,如果不考虑那个特殊操作,到这里已经做完这道题了。
考虑这个特殊操作,需要枚举哪两个相邻的数直接归零。打游戏的时候突发灵感,如果中间两个相邻的数直接归零,左边的操作次数可以贪心得到,右边的呢?也可以同样的贪心得到,所以两个数组l,r。最后ans不断取min。
ans一开始不能太小,这题数据范围太大了,ans开始要开1e18。。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 1e9 + 7;
int a[N], b[N];
int l[N], r[N];
signed main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        b[i] = a[i];
    }
    for (int i = 1; i + 2 <= n; i++){
        l[i] = l[i - 1] + a[i];
        a[i + 1] = max(0ll, a[i + 1] - a[i]);
        a[i + 2] = max(0ll, a[i + 2] - a[i]);
    }
    for (int i = 1; i <= n; i++) a[i]=b[i];
    for (int i = n; i -2 >= 1; i--){
        r[i] = r[i + 1] + a[i];
        a[i - 1] = max(0ll, a[i - 1] - a[i]);
        a[i - 2] = max(0ll, a[i - 2] - a[i]);
    }
    int ans = 1e18;
    for (int i = 1; i + 1 <= n; i++){
        ans = min(l[i - 1] + r[i + 2], ans);
    }
    cout << ans;
    return 0;
}

C. Great Graphs(前缀和+推式子)

题目相当于是给定一个数组,求ai-aj的和,其中j<i。
因为这题的题目背景是图论,思路就一直没有往数学推式子上靠,其实推个式子,答案很简单就出来了。ans=(i-1)*ai - sum(i-1)。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 1e9 + 7;
int a[N], sum[N];
signed main()
{
    int tt;
    cin >> tt;
    while(tt--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        int ans = 0;
        sort(a + 1, a + 1 + n);
        for (int i = 1; i <= n; i++) {
            sum[i] = sum[i - 1] + a[i];
            ans += (i - 1) * a[i] - sum[i - 1] - (a[i] - a[i - 1]);
        }
        cout << -ans << endl;
    }
    return 0;
}

D. PriceFixed(双指针+贪心)

小红要购物,规则是,给定ai表示商品i要多少个,bi表示总购够买商品超过bi后,就可以半价买第i件商品。每件商品如果愿意,可以买无数件。
思路:如果当前可以用优惠买i,那就直接用优惠买;如果不能买,那就原价买bi最大的商品,直到可以用优惠买i。这样是最贪心的,每一分钱都花在了需要的商品上。一开始被可以买无数件误导了,推了三种情况的式子,感觉还挺对,其实是不够贪心的。
两种写法都可以,写法1更简单。两种的内涵是一样的。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 998244353;
struct node{
    int a, b;
    bool operator <(const node &k) const {
        return b < k.b;
    }
} s[N];
signed main()
{
    int n;
    cin >> n;
    int sum = 0, ans = 0;
    for (int i = 1; i <= n; i++){
        scanf("%lld%lld", &s[i].a, &s[i].b);
        sum += s[i].a;
    }
    sort(s + 1, s + 1 + n);
    int l = 1, r = n, k = 0;
    while(k<sum){
        if(k >= s[l].b) {
            ans += s[l].a;
            k += s[l].a;
            l++;
        }
        else {
            /* int mi = min(s[r].a, s[l].b - k);
            ans += mi * 2;
            k += mi;
            s[r].a -= mi;
            if (s[r].a == 0) r--; */
            int cou = s[l].b - k;
            while(cou && l <= r && k < sum){
                int mi = min(cou, s[r].a);
                ans += mi * 2;
                k += mi;
                cou -= mi;
                s[r].a -= mi;
                if(s[r].a==0) r--;
            }
        }
    }
    cout << ans;
    return 0;
}

C. Diluc and Kaeya(gcd+思维)

给定一个只由D和K组成的字符串,问如何分割这个字符串,使得每一个子串中D的数量:K的数量都相等。
既然所有的子串D:K都相等,设其比值为x,那么母串的比值一定也是x。然后这题就简单了。
用map存结构体计数就好啦。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2000 + 5, mod = 998244353;
struct node{
    int x, y;
    bool operator < (const node &k) const {
        if(x != k.x) return x < k.x;
        else return y < k.y;
    }
};
map<node, int> mp;
node f(int d,int k)
{
    if (d == 0) return node{0,1};
    else if (k == 0)return node{1,0};
    int gcd = __gcd(d, k);
    d /= gcd, k /= gcd;
    return node{d, k};
}
signed main()
{
    int tt;
    cin >> tt;
    while(tt--){
        int n;
        cin >> n;
        string s;
        cin >> s;
        s = " " + s;
        int d = 0, k = 0;
        for (int i = 1; i <= n; i++){
            if (s[i]=='D'){
                d++;
                cout << ++mp[f(d, k)] << " ";
            }
            else {
                k++;
                cout << ++mp[f(d, k)] << " ";
            }
        }
        cout << endl;
        mp.clear();
    }
    return 0;
}

D. Secret Santa(拓扑排序+分类讨论)

n个人想送给喜欢的人礼物,给定每个人喜欢的人,但是有的人可以被多个人喜欢,而每个人只能收到一份礼物,也就是说,这多个人中只能有一个人能送礼物给被喜欢的人。所以请你设计一种方案,能让K个人都能送礼物给自己喜欢的人,使K最大化。
思路:如果u喜欢v,那么u与v之间就连接一条有向边,u指向v。类似于拓扑排序,从入度为0的节点t开始做起,设t连接的节点p还没收到礼物,那就分配p给t。因为入度为0,所以没人会主动送礼物给t,因此缓存一下t,最后给再来分配t给别人。
需要注意的是:

  • 既然这是图,是会出现环的,所以最后入度为1的节点也要进行上面的操作。不过这里他们就不用进缓存了,因为是环,入度一定>=1,肯定都能被分配给别人的。
  • 最后是缓存的t ,他们没有收到礼物,但是可能已经送出礼物了,所以t与还没送出礼物的人要互相分配。注意自己不能分配给自己。
    代码写的太丑,不放了。只要能模拟这个思路就好啦。

C. Grandma Capa Knits a Scarf(双指针+枚举)

给定一个字符串,求最少删除多少个字符(必须是同一个字符),能够让字符串变成回文串。
对于一个字符,如果把他删除几个就能得到回文串,那么全部删除他一定也能得到回文串。枚举删除哪一个字符,双指针l,r判断。如果s[l] != s[r],说明l与r中必有一个要被删除。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5, INF = 998244353;
char s[N], t[N];
bool ck(int n)
{
    for (int i = 1; i <= n / 2; i++) {
        if (t[i] != t[n - i + 1]) return 0;
    }
    return 1;
}
signed main()
{
    int tt;
    cin >> tt;
    while(tt--){
        int n;
        cin >> n;
        scanf("%s", s + 1);
        int ans = INF;
        for (char x = 'a'; x <= 'z'; x++) {
            int cnt = 0;
            int ret = 0;
            for (int i = 1; i <= n; i++) {
                if (s[i] != x) t[++cnt] = s[i];
            }
            if (!ck(cnt)) continue;
            int l = 1, r = n;
            while(l < r) {
                if (s[l] == s[r]){
                    l++;
                    r--;
                }
                else if (s[l] == x) {
                    ret++;
                    l++;
                }
                else if (s[r] == x) {
                    ret++;
                    r--;
                }
            }
            ans = min(ans, ret);
            // cout << "ret" << ret << endl;
        }
        if (ans != INF) cout << ans << endl;
        else cout << -1 << endl;
    }
    return 0;
}

B. Omkar and Heavenly Tree(思维)

要求构造一棵n个节点树。给定m个限制,(1<=m< n),要求a到c的路径不可以经过b。
边的情况实在是太多了,考虑点与点之间的关系吧。最多给N-1个点不能作为中间点,那么一定还剩一个点可以作为剩余所有点之间的中间点。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5, INF = 998244353;
signed main()
{
    int tt;
    cin >> tt;
    while(tt--){
        int n, m;
        cin >> n >> m;
        set<int> s;
        for (int i = 1; i <= n; i++) {
            s.insert(i);
        }
        for (int i = 1; i <= m; i++) {
            int x, y, z;
            cin >> x >> y >> z;
            s.erase(y);
        }
        int x = *(s.begin());
        for (int i = 1; i <= n; i++) {
            if (x != i) cout<<i << " "<<x <<endl;
        }
    }
    return 0;
}

C. Omkar and Determination(数据结构+思维)

给定一个由.与X组成的地图。X不可以走通,.与.可以走通,但是只能往左或者往上走。这样子每个点就可以标记为E(能走通)或者N(不能走通),给定m个询问,问l列到r列的地图转化成EN图后,是否能根据EN图还原回地图。
要还原,E一定是.,但是N不一定是X。

EN     .X    .X
NN --> XX 或 X.

所以只要出现左边与上面都是X的情况,那么EN图就是不可还原的了。
只要记录每一列是否有这样的衰点,用前缀和判断就可以了。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, INF = 998244353;
string s[N];
int sum[N];
signed main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        s[i].resize(m + 1);
        scanf("%s", &s[i][1]);
    }
    s[0].resize(m + 1);
    for (int j = 1; j <= m; j++) {
        for (int i = 1; i <= n; i++) {
            if (s[i][j - 1] == 'X' && s[i - 1][j] == 'X') {
                sum[j] = 1;
                break;
            }
        }
        sum[j] = sum[j - 1] + sum[j];
    }
    int q;
    cin >> q;
    while(q--) {
        int l, r;
        scanf("%lld%lld", &l, &r);
        if (sum[r] - sum[l]) puts("NO");
        else puts("YES");
    }
    for (int j = 1; j <= m; j++) sum[j] = 0;
    return 0;
}

D. Another Problem About Dividing Numbers(数论)

给定a和b,每次对a操作或对b操作:对a,选择一个数能被a整除,a/=c;对b,选择一个数c能b整除,b/=c。问恰好k次操作能否让a=b。
将a,b分解质因数,每次的选c操作可看做是在质因数中选几个除去,最后要留下一样的质因数。
那么如果a,b互质,就都有一部分质因数不同,那至少操作2次,如果不互质且a,b不等,那么至少操作一次。

#include <bits/stdc++.h>
using namespace std;
// #define int long long
const int N = 1e6 + 5, INF = 998244353;
int f(int n)
{
    int cnt = 0;
    for (int i = 2; i * i <= n; i++) {
        while(n % i == 0) {
            n /= i;
            cnt++;
        }
    }
    if (n > 1) cnt++;
    return cnt;
}
signed main()
{
    int tt;
    cin >> tt;
    while(tt--){
        int a, b, k;
        scanf("%d%d%d", &a, &b, &k);
        int fa = f(a), fb = f(b);
        int mi = 2, ma = fa + fb;
        if ((a % b == 0 || b % a == 0) && a != b) mi = 1;
        if (mi <= k && k <= ma) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

A. Di-visible Confusion(最小公倍数)

一个数在数组中的下标是i,如果这个数不能被i+1整除,那么这个数就可以删除。
思路:如果2~i + 1中有存在一个数不是ai的因子,那么ai就可以被删除。
我们可以用归纳法来证明。 假设可以删除包含 n-1 个元素的前缀。 由于 an 可以在从 1 到 n 的某个位置被擦除(假设是 k),那么在擦除 n-1 个元素的前缀时,当前缀包含 k-1 个元素时,则 an 在第 k 个位置,所以我们可以在该位置擦除它并相应地擦除序列的其余部分。
所以结合lcm判断一下就好了。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 998244353;
int a[N];
int lc[N];
int lcm(int x, int y)
{
    return x / __gcd(x, y) * y;
}
signed main()
{
    int cnt = 0;
    int l = 1;
    for (int i = 2; i <= 100; i++) {
        l = lcm(i, l);
        if (l > 1e9) {
            cnt = i - 1;
            break;
        }
        lc[i] = l;
    }
    int tt;
    cin >> tt;
    while(tt--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        bool ck = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 2; j <= cnt; j++) {
                if (a[i] % lc[j] == 0 && i <= j - 1) {
                    ck = 1;
                    break;
                } 
            }
            if (ck) break;
        }
        if (ck) puts("NO");
        else puts("YES");
    }
    return 0;
}

D. Moderate Modular Mode(数论+推式子)

求一个整数n使得n%x=y%n。
分类讨论一下,如果x>y,那么直接输出x+y就好了。
这题难点是y>x的情况,首先很容易证明出这种情况下一定是x<=n<=y。接着有:

n = tx + d
y = sn + d
            y + tx
消去d有n =  -------,题目有保证x和y都是偶数,因此s直接取1就可以。
            s + 1
结合上面的不等式n<=y,能得到t<=y/x,所以可以取t = y/x。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, mod = 998244353;
int a[N];
signed main()
{
    int tt;
    cin >> tt;
    while(tt--) {
        int x, y;
        scanf("%lld%lld", &x, &y);
        if (y % x == 0) printf("%lld\n", x);
        else {
            if (x > y) printf("%lld\n", x + y);
            else printf("%lld\n", (y/x*x + y) / 2);
        }
    }
    return 0;
}

盾与战锤(子列和+调和级数)

你有一个长度为 n 的攻击序列,每个攻击都会造成一定伤害,你可以选出它的一个 子序列 进行攻击,每一秒按照子序列中的顺序进行一次攻击。选出的子序列用完了就不能继续攻击了。敌人的盾每k秒会恢复。
先想想怎么选子序列,先思考怎么得到长度为k的最大子列和。这个只要排序数组后求前缀和就好了。(比赛的时候却想了好久)
接下来的就简单了,在盾恢复的前一秒一定是最贪心的,而n以内有很多个恢复时间,此时累加的伤害-盾的总抗伤,取max就好了。
整个复杂度就一调和级数。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5, INF = 998244353;
int a[N], sum[N], ans[N];
signed main()
{
    int n, s;
    cin >> n >> s;
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    sort(a + 1, a + 1 + n, greater<int>());
    for (int i = 1; i <= n; i++) {
        sum[i] = sum[i - 1] + a[i];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j += i) {
            ans[i] = max(ans[i], sum[j] - j / i * s);
        }
        if (n % i != 0) ans[i] = max(ans[i], sum[n] - (n / i + 1) * s);
    }
    for (int i = 1; i <= n; i++) {
        printf("%lld\n", ans[i]);
    }
    return 0;
}

妄想集合(势能线段树)

有n个可重集合,每个集合一开始只有一个数。
操作是往l ~ r加一个数x。然后问 l ~ r之间的数是否可以选三个出来构成三角形。
因为所给的数都不超过1e9,所以说三角形的最大边也就是1e9,而斐波那契数列刚好完美的构不成三角形,这个数列超过46项就超1e9了。
所以一个集合的数超过46,那么这个一个集合就能选出数来构成三角形(根据鸽巢原理)。
一开始只要暴力的往区间加数就好了,然后判断也是不停的暴力。不过询问的区间长度一开始要是超过46,也不用暴力了,直接yes。

#include <bits/stdc++.h>
using namespace std;
// #define int long long
const int N = 1e5 + 5, mod = 998244353;
struct node{
    int l, r;
    int val;
    int add;
    #define l(x) tree[x].l
    #define r(x) tree[x].r
} tree[N * 4];
vector<int> v[N];
char s[7];
void build(int p, int l, int r)
{
    l(p) = l, r(p) = r;
    if (l >= r) {
        tree[p].val = 1;
        return;
    }
    int mid = (l + r) >> 1;
    int chl = p * 2, chr = p * 2 + 1;
    build(chl, l, mid);
    build(chr, mid + 1, r);
    tree[p].val = tree[chl].val + tree[chr].val;
}
void up(int p, int l, int r, int x)
{
    if (l <= l(p) && r(p) <= r && tree[p].val / (r(p) - l(p) + 1) >= 47) {
        return;
    } 
    if (l(p) >= r(p)) {
        v[l(p)].push_back(x);
        tree[p].val++;
        return;
    }
    int mid = (l(p) + r(p)) >> 1;
    int chl = p * 2, chr = p * 2 + 1;
    if (l <= mid) up(chl, l, r, x);
    if (r > mid) up(chr, l, r, x);
    tree[p].val = tree[chl].val + tree[chr].val;
}
int que(int p, int l, int r)
{
    if (l <= l(p) && r(p) <= r) return tree[p].val;
    int mid = (l(p) + r(p)) >> 1;
    int chl = p * 2, chr = p * 2 + 1;
    int ret = 0;
    if (l <= mid) ret += que(chl, l, r);
    if (r > mid) ret += que(chr, l, r);
    return ret;
}
bool ck(int l, int r)
{
    vector<int> t;
    for (int i = l; i <= r; i++) {
        for (auto p : v[i]) t.push_back(p);
    }
    sort(t.begin(), t.end());
    int len = t.size();
    for (int i = 0; i + 2 <= len - 1; i++) {
        if (t[i] + t[i + 1] > t[i + 2]) return true;
    }
    return false;
}
signed main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int x;
        scanf("%d", &x);
        v[i].push_back(x);
    }
    build(1, 1, n);
    while(m--){
        int l, r;
        scanf("%s%d%d", s + 1, &l, &r);
        if (s[1] == 'A'){
            int len = que(1, l, r);
            if (len >= 47) puts("YES");
            else {
                if (ck(l, r) == true) puts("YES");
                else puts("NO");
            }
        }
        else {
            int x;
            scanf("%d", &x);
            up(1, l, r, x);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值