[CF1083F]The Fair Nut and Amusing Xor[差分+同余分类+根号分治+分块]

题意

给定两个长度为 \(n\) 的序列 \(\{a_i\}\) 与 \(\{b_i\}\),你需要求出它们的相似度。,我们定义这两个序列的相似度为将其中一个序列转化为另一个序列所需的最小操作次数。一次操作定义如下: - 选择序列中的一个长度为 \(k\) 的子段,将子段内的所有元素异或上一个相同的值 \(x\)\(x\) 可以任意决定。特殊地,若其中一个序列无论如何都不能转化到另一个序列,那么这两个序列的相似度为 \(-1\)。 这之后,将会有 \(q\) 次修改操作,单次操作格式如下:

s p v\(s\) 是一个小写字符,且要么为 a,要么为 b。若 \(s =\) a,则表明将 \(a_p\) 的值修改为 \(v\);若 \(s =\) b,则表明将 \(b_p\) 的值修改为 \(v\)

在每一次修改操作结束后,你也需要求出两个序列的相似度。输出最开始及每次修改后的答案。

\(1 \leq k \leq n \leq 2 \times 10^5, 0 \leq q \leq 2 \times 10^5, 1 \leq p \leq n, 0 \leq a_i, b_i, v < 2^{14}\),输入保证给出的两个序列长度均为 \(n\)

分析

  • 先考虑暴力:每次暴力修改,然后贪心地从第一个位置开始推,要修改就修改。

  • 考虑差分,这样每次修改的位置就只有两个了。

  • 将所有位置按照模 \(k\) 分组,每一组互不干扰。我们记某组中连续的三个位置为 1,2,3,\(c\) 数组表示 \(a\)\(b\) 的差分数组的异或结果。每个位置需要异或的值就是 \(c_1; c_1\ xor\ c_2;c_1\ xor\ c_2\ xor c_3\),所以如果有解,每组的异或值都要为0.

  • 讨论 \(k\) 的大小来修改:

    如果 \(k > \sqrt n\) ,每组个数 \(\frac{n}{k}<\sqrt n\) ,暴力即可, 复杂度 \(O(n\sqrt n)\)

    如果 \(k<\sqrt n\) ,组数 \(k<\sqrt n\) ,每组分块维护,复杂度 \(O(n\sqrt n)\)

  • 总时间复杂度为 \(O(n\sqrt n)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
#define re(x) memset(x, 0, sizeof x)
inline int gi() {
    int x = 0,f = 1;
    char ch = getchar();
    while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
    return x * f;
}
template <typename T> inline void Max(T &a, T b){if(a < b) a = b;}
template <typename T> inline void Min(T &a, T b){if(a > b) a = b;}
const int N = 2e5 + 7, gg = 400;
int n, k, q, ans, cnt, ndc;
int a[N], b[N], c[N], pre[N], bl[N], L[N], tag[N];
char s[N];
int num[1007][17000];
void upd1(int p, int v) {
    for(int i = p; i <= n; i += k) {
        pre[i] ^= v;
        if(v && pre[i] == 0) --ans;
        if(v && pre[i] == v) ++ans;
    }
    int lst = p % k + ((n - p % k) / k) * k;
    if(v && pre[lst] == 0) --cnt;
    if(v && pre[lst] == v) ++cnt;
}
void upd2(int p, int v) {
    for(int i = p; bl[i] == bl[p]; i += k) {
        if(v && (pre[i] ^ tag[bl[p]]) == 0) ++ans;
        num[bl[p]][pre[i]] --;
        pre[i] ^= v;
        num[bl[p]][pre[i]] ++;
        if(v && (pre[i] ^ tag[bl[p]]) == 0) --ans;
    }
    for(int i = bl[p] + 1; i <= ndc && L[i] % k == p % k; ++i) {
        if(v) ans += num[i][tag[i]];
        tag[i] ^= v;
        if(v) ans -= num[i][tag[i]];
    }
    int lst = p % k + ((n - p % k) / k) * k;
    if(v && (pre[lst] ^ tag[bl[lst]]) == 0) --cnt;
    if(v && (pre[lst] ^ tag[bl[lst]]) == v) ++cnt;
}
void modify(int p, int v) {
    if(n / k < gg)
        upd1(p, v ^ c[p]);
    else
        upd2(p, v ^ c[p]);
    c[p] = v;
}
int main() {
    n = gi(), k = gi(), q = gi();
    rep(i, 1, n) a[i] = gi();
    rep(i, 1, n) b[i] = gi();
    ++n;
    
    if(n / k >= gg)
    for(int i = 1; i <= k; ++i)
    for(int j = i; j <= n; j += k) {
        if(((j - 1) / k + 1) % gg == 1) {
            L[++ndc] = j;
            num[ndc][0] = min(gg, (n - j) / k + 1);
        }
        bl[j] = ndc;
    }
    rep(i, 1, n) modify(i, (a[i] ^ a[i - 1]) ^ (b[i] ^ b[i - 1]));
    printf("%d\n", cnt ? -1 : ans);
    int i, v;
    while(q--) {
        scanf("%s%d%d", s, &i, &v);
        if(s[0] == 'a') a[i] = v;
        else b[i] = v;
        modify(i, (a[i] ^ a[i - 1]) ^ (b[i] ^ b[i - 1]));
        if(i != n) modify(i + 1, (a[i + 1] ^ a[i]) ^ (b[i + 1] ^ b[i]));
        printf("%d\n", cnt ? -1 : ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/yqgAKIOI/p/10212228.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值