Codeforces Round #815 (Div. 2) 比赛记录

前言

摆了一个学期的烂了。。
之前都在打Atcoder 没有怎么打过cf。
暑假集训让我认清了和其他ACMer的差距,决定开始刷每一场cf!!!
为了最后一个ACM赛季不留遗憾(尽量)。。

A. Burenka Plays with Fractions

题目链接
对于 a b a\over b ba = c d c\over d dc 的,操作次数是0。
对于 a = 0 a = 0 a=0 或者 b = 0 b=0 b=0 的,操作次数最多1次。
对于 a b a\over b ba c d c\over d dc 成倍数关系的,操作次数最多1 次。
其他的情况操作2次。
判断倍数关系,建议使用 [ a × d     M O D    ( c × b ) ] [a\times d ~~~MOD~~(c\times b)] [a×d   MOD  (c×b)](a、d和c、b互换位置同理)

B. Interesting Sum

题目链接
设序列最大值为 M a x N MaxN MaxN,次大值为 S M a x N SMaxN SMaxN
最小值为 M i n N MinN MinN ,次小值为 S M i n N SMinN SMinN
无论这4个值在原序列中的相对顺序是什么。
一定有办法框选连续的一段区间使得区间最大值是次大值和最大值中的其中一个,区间最小值是次小值和最小值中的其中一个。
所以答案为 S M a x N SMaxN SMaxN + M a x N MaxN MaxN - S M i n N SMinN SMinN - M i n N MinN MinN
排序即可求得结果。

C. Corners

题目链接
这题由于没有初始化 Wrong Answer On test4了2 发,属实非常亏。
最佳情况是每一个1 都对答案有一次操作的贡献。
如果原矩阵全为1,那么至少有2个 1 不会对答案产生贡献。
如果只有一个 0,其余全为 1 ,那么也至少有 1个 1 不会对答案产生贡献。
如果原矩阵由2个或者以上的 0 构成,仔细观察,不难发现,如果存在以下结构,那么所有的 1 都会对答案产生贡献。

结构1
01
10
结构2
00
11
结构3
10
10
对称的结构同理

D.Xor-Subsequence

题目链接
题目大意
一个长度为 n n n 序列 a a a,求最长美丽的序列 b b b( l e n ( b ) ≤ n len(b)\le n len(b)n)
序列 b b b 要满足

  • 0 ≤ b 0 < b 1 < b 2 < . . < b m ≤ n 0\le b_0 \lt b_1\lt b_2 \lt..\lt b_m \le n 0b0<b1<b2<..<bmn
  • a b p    X O R    b p + 1 < a b p + 1    X O R    b p a_{b_p}~~XOR~~b_{p+1}\lt a_{b_{p + 1}}~~XOR ~~b_p abp  XOR  bp+1<abp+1  XOR  bp

也就是在序列 a a a 中获取一个最长的子序列,满足前一个元素的值异或后一个元素的原序列下标小于后一个元素的值异或前一个元素的原序列下标。
easy version 的做法类似暴力的最长上升子序列的dp转移,对于 dp[i],只要转移 dp[i - 200]~dp[i -1] 即可。


正解
建Trie树,插入每一个 a i    X O R    i a_i~~XOR ~~i ai  XOR  i
继续沿用最长上升子序列问题的转移思路,并且拆位考虑转移。
位置 j j j 能否转移至位置 i i i ,也就是对于 b p = j     b p + 1 = i b_p = j~~~b_{p+1}=i bp=j   bp+1=i是否满足题目要求。
从高位依次往地位判断,对于第 p p p 个数位,若 [ p ] a j    X O R    j = [ p ] a i    X O R    i [p]a_j~~XOR~~j=[p]a_i~~XOR~~i [p]aj  XOR  j=[p]ai  XOR  i,那么数位 p p p 上,也满足 [ p ] a j    X O R    i = [ p ] a i    X O R    j [p]a_j~~XOR~~i=[p]a_i~~XOR~~j [p]aj  XOR  i=[p]ai  XOR  j
如果第 p p p 个数位, [ p ] a j    X O R    j ≠ [ p ] a i    X O R    i [p]a_j~~XOR~~j\ne [p]a_i~~XOR~~i [p]aj  XOR  j=[p]ai  XOR  i,那么需要满足 [ p ] a j    X O R    i < [ p ] a i    X O R    j [p]a_j~~XOR~~i\lt [p]a_i~~XOR~~j [p]aj  XOR  i<[p]ai  XOR  j的条件, 就需要:
如果 [ p ] a i = 1 [p]a_i = 1 [p]ai=1 则需要满足 [ p ] j = 0 [p]j=0 [p]j=0,反之需要满足 [ p ] j = 1 [p]j=1 [p]j=1
我们发现只要在Trie树上,分别维护 [ p ] j = 0 、 [ p ] j = 1 [p]j=0、[p]j =1 [p]j=0[p]j=1 的最长子序列信息,对 [ p ] a i = 0 ∣ 1 [p]a_i=0|1 [p]ai=0∣1讨论进行转移即可。

参考代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 20;
int tree[N * 30][2];
int dp[N * 30][2];
int tot;
int n;
int a[N];
 
int get_ans(int xorval, int val) {
    int u = 0;
    int res = 0;
    for (int i = 30; i >= 0; i -- ) {
        int xb = ((xorval >> i) & 1);
        int b = ((val >> i) & 1);
        res = max(dp[tree[u][xb ^ 1]][b ^ 1], res);
        if (tree[u][xb]) {
            u = tree[u][xb];
        } else {
            break;
        }
    }
    return res + 1;
}
void inst(int xorval, int pos, int upv) {
    int u = 0;
    for (int i = 30; i >= 0; i -- ) {
        int xb = (xorval >> i) & 1;
        int pb = ((pos >> i) & 1);
        if (tree[u][xb]) {
            u = tree[u][xb];
            dp[u][pb] = max(dp[u][pb], upv);
        } else {
            tree[u][xb] = ++ tot;
            u = tree[u][xb];
            dp[u][pb] = max(dp[u][pb], upv);
        }
    }
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t --) {
        cin >> n;
        for (int i = 1; i <= n; i ++ ) {
            cin >> a[i];
        }
        tot = 0;
        for (int i = 0; i <= n * 30; i ++ ) {
            tree[i][1] = tree[i][0] = dp[i][1] = dp[i][0] = 0;
        }
        int ans = 0;
        for (int i = 1; i <= n; i ++ ) {
            int tmp = get_ans(a[i] ^ (i - 1), a[i]);
            ans = max(ans, tmp);
            inst(a[i] ^ (i - 1), i - 1, tmp);
        }
        cout << ans << '\n';
    }
}

E. Misha and Paintings

题目链接
结论:
矩阵是正方形(square)。
如果原矩阵内,颜色数量 c n t cnt cnt 少于 k k k,那么一次操作最多可以增加一种颜色。
答案就是 k − c n t k-cnt kcnt
否则,如果原矩阵内颜色数量 c n t cnt cnt等于 k k k,答案为0。
其余情况均为 1 或者 2。
对于 1 或者 2 的情况,我们可以在矩形内任意画一个最大的边长为 L L L L < n L\lt n L<n 的矩阵,全部涂上一种颜色,使得剩下的颜色种类恰好大于 k k k。(如果可以等于 k k k , 那么就已经是答案了)
然后再以这个最大矩阵的右下角的右下一格为右下角,画一个小矩阵,涂上一种(另一种)的颜色。(如下图2的位置)逐渐扩大这个矩阵。

XXXXX
X111X
X111X
X111X
XXXX2
-----------
扩大一格:
XXXXX
X111X
X111X
X1122
XXX22

这个小矩阵每扩大1,颜色种类就会减少1,2或者0。
那么总会存在对应颜色种类剩余 k − 1 k-1 k1 或者 k k k 的情况。
这个时候只要对小矩阵的颜色稍作调整,就会构造出刚好 剩余 k k k 种颜色的情况。

现在,我们需要统计画一个边长为 L L L 矩阵, 涂上同一种颜色,颜色种类数刚好为 k k k 的方案的存在。

对于一种颜色,被什么样子的矩阵覆盖后,这种颜色在原矩阵中消失:我们需要记录每一种颜色在原矩阵中的最上,最下,最左,最右出现的位置即可。
然后我们会发现,能覆盖一种颜色的矩阵的左上角一定出现在原矩阵中某个矩形的范围内。
这样,我们就可以通过二维差分,来统计以某个点作为左上角画边长为 L L L的矩阵,能覆盖的颜色种类数,剩余的颜色种类数也自然可以得出。
枚举 矩阵边长 L L L ,以及颜色种类 n 2 n^2 n2
总的时间复杂度大约是 O ( n 3 ) O(n^3) O(n3)
参考代码

#include <bits/stdc++.h>
using namespace std;
const int N = 502;
int mrx[N][N];
int n, k;
int xmx[N * N], xmn[N * N], ymx[N * N], ymn[N * N];
int sum[N][N];
int apper[N * N];
int cnt;
int main() {
    cin >> n >> k;
    for (int i = 1; i <=  n * n; i ++ ) ymn[i] = xmn[i] = n + 1;
    for (int i = 1 ; i <= n; i ++ ) {
        for (int j = 1; j <= n; j ++ ) {
            cin >> mrx[i][j];
            if (apper[mrx[i][j]] == 0) cnt ++;
            apper[mrx[i][j]] = 1;
            xmn[mrx[i][j]] = min(xmn[mrx[i][j]], i);
            xmx[mrx[i][j]] = max(xmx[mrx[i][j]], i);
            ymn[mrx[i][j]] = min(ymn[mrx[i][j]], j);
            ymx[mrx[i][j]] = max(ymx[mrx[i][j]], j);
        }
    }
    if (k >= cnt) {
        cout << k - cnt << endl;
    } else {
        for (int L = 1; L < n; L ++ ) {
            for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= n; j ++) sum[i][j] = 0;
            for (int i = 1; i <= n * n; i ++ ) {

                if (!apper[i] || ymx[i] - ymn[i] + 1 > L || xmx[i] - xmn[i] + 1 > L) continue;
                int leastx = max(1, xmx[i] - L + 1);
                int leasty = max(1, ymx[i] - L + 1);
                int mostx = xmn[i];
                int mosty = ymn[i];

                sum[leastx][leasty] += 1;
                sum[leastx][mosty + 1] -=1;
                sum[mostx + 1][leasty] -= 1;
                sum[mostx + 1][mosty + 1] += 1;

            }
            for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= n; j ++ ) sum[i][j] += sum[i - 1][j];
            for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= n; j ++ ) sum[i][j] += sum[i][j - 1];
            for (int i = 1; i <= n; i ++ ) {
                for (int j = 1; j <= n;j ++) {
                    if (cnt - sum[i][j] == k || cnt - sum[i][j] == k - 1) {
                        cout << 1 << endl;
                        return 0;
                    }
                }
            }
        }
        cout << 2 << endl;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值