1017考试总结

1017考试总结

T1

​ 题目大意:

​ 一个长度为n的非负整数序列x,满足m个条件:第i个条件为\(x[li]\ or \ x[li+1]or...or\ x[ri]=pi\)。他想知道是否存在一个序列满足条件,如果存在,他还要构造出一个这样的序列。如果存在这样的序列x,第一行输出Yes,第二行输出n个不超过2^30-1的非负整数表示x[1]~x[n],否则输出一行No。对于100%的数据,n,m≤100,000,1 ≤ li ≤ ri ≤ n,0 ≤ pi < 2 ^ 30。

​ 二进制。

​ 我们考虑在二进制上一位一位的搞。对于一个条件\([l_i, r_i] p_i\),假设\(p_i\)\(k\)位上为0,那么这个区间里所有数的第\(k\)位上都是0,我们把\(f[l_i ...r_i][k] ++\)。如果说\(f[i][j] > 0\),这说明\(x[i]\)这个数第\(j\)位上一定为0,我们让\(num[i][j] = 0\),如果\(f[i][j] == 0\),那么\(num[i][j] = 1\)

​ 这样每个数就都可以表示出来了。

​ 那怎么判断无解呢?用\(sum[i][j]\)表示前\(i\)个数第\(j\)位上1的个数,对于每一个条件\([l_i, r_i] p_i\),判断\(sum[r_i][j] - sum[l_i - 1][j]\)\(p_i\)的第\(j\)位是否匹配就好了。

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 1e5 + 5;
int n, m, tag;
int f[N][31], sum[N][31], num[N][31];
struct line { int l, r, p; } a[N];

void calc_f(int l, int r, int p) {
    for(int i = 0;i <= 30; i++) {
        if(!((p >> i) & 1)) f[l][i] ++, f[r + 1][i] --; 
    }
}

int judge(int l, int r, int p) {
    for(int i = 0;i <= 30; i++) {
        int tmp = sum[r][i] - sum[l - 1][i];
        if((!((p >> i) & 1) && tmp > 0) || (((p >> i) & 1) && tmp == 0)) return 0; 
    }
    return 1;
}

int calc_num(int x) {
    int res = 0;
    for(int i = 0;i <= 30; i++) {
        if(num[x][i] == 1) res += (1 << i);
    }
    return res;
}

int main() {

    n = read(); m = read();
    for(int i = 1;i <= m; i++) {
        a[i].l = read(), a[i].r = read(), a[i].p = read();
        calc_f(a[i].l, a[i].r, a[i].p);
    }
    for(int i = 0;i <= 30; i++) 
        for(int j = 1;j <= n; j++) f[j][i] += f[j - 1][i];
    for(int i = 0;i <= 30; i++) 
        for(int j = 1;j <= n; j++) {
            if(f[j][i] > 0) num[j][i] = 0;
            else num[j][i] = 1;
        }
    for(int i = 0;i <= 30; i++) 
        for(int j = 1;j <= n; j++) 
            sum[j][i] = sum[j - 1][i] + num[j][i];
    for(int i = 1;i <= m; i++) 
        if(!judge(a[i].l, a[i].r, a[i].p)) { tag = 1; break; }
    if(tag) printf("No\n");
    else {
        printf("Yes\n");
        for(int i = 1;i <= n; i++) printf("%d ", calc_num(i));
    }
    
    return 0;
}

T2

​ 题目大意:

​ 两个字符串s,t,其中s只包含小写字母以及\(*\),t只包含小写字母。可以进行任意多次操作,每次选择s中的一个\(*\),将它修改为任意多个(可以是0个)它的前一个字符。他想知道是否能将s修改为t。如果能将s修改为t,输出Yes,否则输出No。对于100%的数据,T≤100,|s|,|t|≤30000。

​ 双指针 + 模拟。

​ 用两个指针\(t, k\)表示当先字符串\(s, t\)枚举到了那个字符。

​ 如果当前枚举到的两个字符不相等,输出No。如果当前枚举到的\(s\)的字符是\(x\), \(x\)的下一个字符是\(*\),直接跳过,一直跳到连续的最后一个\(x\),并且统计\(x\)的个数\(num1\),同样的字符串\(t\)也要往后跳,也是跳到连续的最后一个\(x\),统计\(x\)的个数\(num2\)。如果\(num1 > num2\),输出No,否则往下找,重复这个过程。

#include <bits/stdc++.h>

using namespace std;

int T, n, m;
char a[30005], b[30005];

int main() {

    cin >> T; 
    while(T --> 0) {
        cin >> a >> b;
        n = strlen(a); m = strlen(b);
        int t = 0, k = 0, flag = -1;
        while(t <= n || k <= m) {
            if(a[t] != b[k]) {
                printf("No\n"); flag = 1; break; 
            } 
            else {
                int num1 = 0, num2 = 0;
                if(a[t + 1] == '*') {
                    char ch = a[t];
                    while(t <= n && (a[t + 1] == '*' || a[t + 1] == ch)) {
                        if(a[t + 1] == ch) num1 ++; t++;
                    }
                    while(k <= m && b[k + 1] == ch) k ++, num2 ++;
                    if(num1 > num2) {
                        printf("No\n"); flag = 1; break; 
                    }
                }
            }
            t ++; k ++;
        }
        if(flag == -1) printf("Yes\n");
    }
    
    return 0;
}

T3

​ 题目大意:

​ 一个3×n的数组,要在每一列选一个数(或者不选),满足以下条件:

​ (1)如果在第一行选,那它必须大于等于上一个数

​ (2)如果在第二行选,那么必须小于等于上一个数

​ (3)如果在第三行选,对于连续的一段在第三行选的数,必须满足方向相同(都小于等于上一个数或者都大于等于上一个数)。

​ 对于100%的数据, n <= 100000, m <= 1000000000

洛谷链接

数据结构优化DP。

​ 我们先设4种状态:1:选第一行的数;2:选第二行的数;3:选第三行的数并且单调不降;4:选第三行的数并且单调不升。

\(dp[i][j]\)表示第\(j\)列状态为\(i\)的最大合法长度:那么状态转移方程是:\(dp[i][j] = max(dp[i'][k] + 1)\)

\(k < i\),对于\(i = 1, 2\)时,\(i' = 1, 2, 3, 4\)都可以;对于\(i = 3\)时,\(i' = 1, 2, 3\);对于\(i = 4\)时,\(i' = 1, 2, 4\)

​ 暴力枚举\(k\)\(O(n ^ 2)\)的,用线段树维护最大值则可以优化到\(O(n log n)\)

#include <bits/stdc++.h>

#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 1e5 + 5;
int n, cnt, ans;
int a[N], b[N], c[N], disc[N << 2];
int dp[5][N], t[5][N << 4];

void insert(int o, int l, int r, int x, int val, int id) {
    if(l == r) { t[id][o] = max(t[id][o], val); return ; }
    if(x <= mid) insert(ls(o), l, mid, x, val, id);
    if(x > mid) insert(rs(o), mid + 1, r, x, val, id);
    t[id][o] = max(t[id][ls(o)], t[id][rs(o)]);
}

int query(int o, int l, int r, int x, int y, int id) {
    if(x <= l && y >= r) { return t[id][o]; }
    int res = 0;
    if(x <= mid) res = max(res, query(ls(o), l, mid, x, y, id));    
    if(y > mid) res = max(res, query(rs(o), mid + 1, r, x, y, id));
    return res;
}

int main() {

    n = read();
    for(int i = 1;i <= n; i++) a[i] = read(), disc[++ cnt] = a[i];
    for(int i = 1;i <= n; i++) b[i] = read(), disc[++ cnt] = b[i];
    for(int i = 1;i <= n; i++) c[i] = read(), disc[++ cnt] = c[i];
    sort(disc + 1, disc + 1 + cnt);
    cnt = unique(disc + 1, disc + cnt + 1) - disc - 1;
    for(int i = 1;i <= n; i++) a[i] = lower_bound(disc + 1, disc + cnt + 1, a[i]) - disc;
    for(int i = 1;i <= n; i++) b[i] = lower_bound(disc + 1, disc + cnt + 1, b[i]) - disc;
    for(int i = 1;i <= n; i++) c[i] = lower_bound(disc + 1, disc + cnt + 1, c[i]) - disc;
    for(int i = 1;i <= n; i++) {
        dp[1][i] = max(dp[1][i], query(1, 1, cnt, 1, a[i], 1));
        dp[1][i] = max(dp[1][i], query(1, 1, cnt, 1, a[i], 2));
        dp[1][i] = max(dp[1][i], query(1, 1, cnt, 1, a[i], 3));
        dp[1][i] = max(dp[1][i], query(1, 1, cnt, 1, a[i], 4));
        dp[1][i] ++;
        dp[2][i] = max(dp[2][i], query(1, 1, cnt, b[i], cnt, 1));
        dp[2][i] = max(dp[2][i], query(1, 1, cnt, b[i], cnt, 2));
        dp[2][i] = max(dp[2][i], query(1, 1, cnt, b[i], cnt, 3));
        dp[2][i] = max(dp[2][i], query(1, 1, cnt, b[i], cnt, 4));
        dp[2][i] ++;
        dp[3][i] = max(dp[3][i], query(1, 1, cnt, 1, c[i], 1));
        dp[3][i] = max(dp[3][i], query(1, 1, cnt, 1, c[i], 2));
        dp[3][i] = max(dp[3][i], query(1, 1, cnt, 1, c[i], 3));
        dp[3][i] ++;
        dp[4][i] = max(dp[4][i], query(1, 1, cnt, c[i], cnt, 1));
        dp[4][i] = max(dp[4][i], query(1, 1, cnt, c[i], cnt, 2));
        dp[4][i] = max(dp[4][i], query(1, 1, cnt, c[i], cnt, 4));
        dp[4][i] ++;
        insert(1, 1, cnt, a[i], dp[1][i], 1); insert(1, 1, cnt, b[i], dp[2][i], 2);
        insert(1, 1, cnt, c[i], dp[3][i], 3); insert(1, 1, cnt, c[i], dp[4][i], 4);
    }
    for(int i = 1;i <= n; i++) 
        for(int j = 1;j <= 4; j++) 
            ans = max(ans, dp[j][i]);
    printf("%d", ans);

    return 0;
}
posted @ 2020-10-17 21:16  C锥  阅读(119)  评论(0编辑  收藏  举报