zoukankan      html  css  js  c++  java
  • [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  imes 10^5, 0 leq q leq 2  imes 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(nsqrt n))

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

    • 总时间复杂度为 (O(nsqrt 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
    ", 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
    ", cnt ? -1 : ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    mount: can't find cdrom in /etc /fstab or /etc/mtab
    Crontab 的使用方法
    遍历文件夹下的所有文件
    Python将PDF转为TXT
    软件下载链接地址
    在机器上连接数据库
    正则表达式
    python实现给字典添加条目的方法
    urllib2连接超时设置
    数据结构(二)链表
  • 原文地址:https://www.cnblogs.com/yqgAKIOI/p/10212228.html
Copyright © 2011-2022 走看看