• CF1320 Div1 D.Reachable Strings 题解


    题目大意

    给定一个长为(n)的01串(S),每次你可以对一个串的三个连续位置做:(011 ightarrow 110),(110 ightarrow 011)的操作。

    (q)次询问,每次询问给出两个长度相等的子串,问是否能从一个串变到另一个串。

    题解

    首先,我们发现操作不改变(1)的个数。所以可以先用前缀和判断(1)的个数是否相等。

    如果某个字符串不出现相邻的两个(1),那么容易得到你无法做任何有效的操作,就直接判断是否相等。这一步可以用hash或sa或sam实现。

    否则我们又发现一个新的不变量,就是奇数位(是子串的奇数位)的(1)的个数和偶数位的(1)的个数都不改变!所以我们用前缀和算出两个串判断奇数位的(1),再判断是否相等

    即可

    提交

    获得WA5的好成绩!

    这给我们一个启示,就是简单的使用不变量无法得到正确的充要条件。

    我们需要一个绝妙的想法。

    (0)看成小人,(1)看成空地。每次操作可以理解成把一个小人移动两个单位长度,且不改变小人之间的相对位置。

    因此,从左到右每个小人所处位置的奇偶性是不变的。

    设询问的字符串为([l_1, r_1], [l_2, r_2]),则先判断这两个子串的(0)的个数是否相等。如果相等,记它们为(k)。设从左往右第一个串的第(i)(0)在S的位置是(a_i),第二个串是(b_i)

    充要条件就是:对任意(i)(a_i - l_1 equiv b_i - l_2 (mod 2))

    (至于条件的充分性,真的很好证的,就不写了)

    我们把S中每个(0)连起来,然后如果这个(0)的下标为奇数,就在这个位置填上(1),否则填上(0),记这个新串是(T_1),把这个新串的(01)互换,形成(T_2)

    (T = T_1T_2)建后缀数组或后缀自动机。问题变成了每次询问(T)的两个子串是否相等。

    这是一个经典的问题。

    #include <bits/stdc++.h>
    #define debug(x) cerr << #x << " " << (x) << endl
    using namespace std;
     
    const int N = 200005, K = 25;
     
    int n, q, len[N << 2], par[N << 2], last = 0, cnt = 0;
    char str[N];
    map<char, int> ch[N << 2];
    void extend (char c) {
    	int p = last, np = ++cnt;
    	len[np] = len[p] + 1;
    	for (; ~p && !ch[p][c]; p = par[p]) ch[p][c] = np;
    	if (p < 0) par[np] = 0;
    	else {
    		int q = ch[p][c];
    		if (len[q] == len[p] + 1) par[np] = q;
    		else {
    			int nq = ++cnt;
    			ch[nq] = ch[q], len[nq] = len[p] + 1;
    			par[nq] = par[q], par[q] = par[np] = nq;
    			for (; ~p && ch[p][c] == q; p = par[p]) ch[p][c] = nq;
    		}
    	}
    	last = np;
    }
     
    int tot = 0, id[N << 1], fa[N << 2][K], Log2[N << 2];
    int zero[N << 1], pos[N];
     
    int find_pos (int l, int r) {
    	int u = id[r];
    	for (int i = Log2[cnt]; i >= 0; i--) {
    		if (~fa[u][i] && len[fa[u][i]] > r - l) u = fa[u][i];
    	}
    	return u;
    }
     
    int main () {
    	scanf("%d%s", &n, &str);
    	par[0] = -1, len[0] = 0;
    	for (int i = 0; i < n; i++) {
    		if (str[i] == '0') {
    			pos[tot] = i;
    			zero[tot++] = i & 1;
    		}
    	}
    	
     
    	for (int i = 0, j = 0; i < n; i++) {
    		if (str[i] == '0') {
    			zero[tot + j] = i & 1 ^ 1;
    			j++;
    		}
    	}
     
    	for (int i = 0; i < (tot << 1); i++) extend(zero[i] + '0'), id[i] = last;
     
    	Log2[1] = 0;
    	for (int i = 2; i <= cnt; i++) Log2[i] = Log2[i >> 1] + 1;
    	for (int i = 0; i <= cnt; i++) fa[i][0] = par[i];
    	for (int i = 1; i <= Log2[cnt]; i++) {
    		for (int j = 0; j <= cnt; j++) {
    			if (fa[j][i - 1] < 0) fa[j][i] = -1;
    			else fa[j][i] = fa[fa[j][i - 1]][i - 1];
    		}
    	}
     
    	scanf("%d", &q);
    	for (int i = 0; i < q; i++) {
    		int l1, l2, len;
    		scanf("%d%d%d", &l1, &l2, &len), l1--, l2--;
     
    		int L1 = lower_bound(pos, pos + tot, l1) - pos, R1 = lower_bound(pos, pos + tot, l1 + len) - pos;
    		int L2 = lower_bound(pos, pos + tot, l2) - pos, R2 = lower_bound(pos, pos + tot, l2 + len) - pos;
     
    		bool flag = true;
    		if (R1 - L1 != R2 - L2) flag = false;
    		if (l1 & 1) L1 += tot, R1 += tot;
    		if (l2 & 1) L2 += tot, R2 += tot;
    		if (L1 < R1 && L2 < R2 && find_pos(L1, R1 - 1) != find_pos(L2, R2 - 1)) flag = false;
    		if (flag) puts("Yes");
    		else puts("No");
    	}
    	return 0;
    }
    
  • 相关阅读:
    关于一个标准的poi下载文件模板 可拿来来直接使用
    关于史上最简单的导出(导出excal)
    关于spring MVC的面试题
    关于响应式布局(pc端访问自适应 手机端访问自适应)
    密码生成器
    关于poi的导出(POI)
    关于MUI一个很实用的前端框架
    (转载)插画版Kubernetes指南(小孩子也能看懂的kubernetes教程)
    (转载)Python tips: 什么是*args和**kwargs?
    python编程中的if __name__ == 'main': 的作用和原理
  • 原文地址:https://www.cnblogs.com/mathematician/p/12516909.html
Copyright © 2020-2023  润新知