E2. Maldición imperdonable (versión difícil)

Problema - E2 - Codeforces

Descripción del problema: Dadas dos cadenas y una k. Si los subíndices i y j satisfacen |i - j| == k or |i - j| == k + 1, entonces sí swap(s[i], s[j]), s es una de las dos cadenas.

Idea: si <i,z> <z,j>funciona, entonces <i,j>debe ser factible. Se descubre que para un bloque conectado, los caracteres de este bloque conectado siempre se pueden intercambiar. Si para un bloque conectado, los números correspondientes a los tipos de caracteres de las dos cadenas en el bloque conectado son los mismos, entonces siempre se pueden realizar varias operaciones para hacer que los subíndices correspondientes de las dos cadenas en el bloque conectado sean iguales.

Por lo tanto, dfs encuentra bloques conectados y luego juzga si los tipos de caracteres en este bloque conectado y sus números correspondientes son iguales. Si son iguales, pueden ser iguales, y si no son iguales, no deben ser iguales.

Código CA:

const int N = 2e5 + 21;
char s1[N], s2[N];
int fa[N]; // 并查集
// vis数组是判断连通块是否遍历过,st是判断这个点是否在找连通块中被遍历过
bool vis[N], st[N];
int find(int u) {
    
    return fa[u] == u ? u : fa[u] = find(fa[u]); }
void inpfile();
void solve() {
    
    
    int n,k; cin>>n>>k;
    // 初始化
    for(int i = 1;  i <= n; ++i) fa[i] = i, vis[i] = 0, st[i] = 0;
    cin>>s1 + 1>> s2 + 1;
    bool fg = true;
    map<int,int> mii;
    // 找连通块
    auto dfs = [&] (auto &&dfs, int u) -> void {
    
    
        if(st[u]) return ;
        st[u] = 1;
        int len = 0;
        for(int j = 0; j < 4; ++j) {
    
    
            len = (k + j%2) * (j < 2 ? 1 : -1);
            int pos = u + len;
            // 上面操作是得到 位置 
            // pos - k  |  pos + k
            // pos + k + 1 |  pos - (k + 1)

            // 位置不合法
            if(pos < 1 || pos > n) continue;

            // 找父亲
            int pfa = find(pos), ifa = find(u);
            if(pfa != ifa) {
    
    
                // 注意一定是将 pos的指向u的。因为后面遍历时,是根据第一个的位置来判断的
                fa[pfa] = ifa;
                /**
                 * 1 -- > 2 --> 3 -- > 4
                 * 将2的父亲指向1的父亲,这样在后面判断是否遍历过就行。
                 * 
                 * 由于我用的vis数组判断,如果用的是用map或者什么的,将每个点都进行个判断也行
                */

               // 判断字符种类及其个数是否相同
               // 这里用 一个字符串进行++操作,一个字符串进行--操作,等价于次
                mii[s1[pos]] ++;
                mii[s2[pos]] --;
                dfs(dfs, pos);
            }
        }
    };
    for(int i = 1; i <= n; ++i) {
    
    
        // 由于我是用这个父亲值进行判断的
        if(vis[find(i)]) continue;
        vis[find(i)] = 1;
        // 每次要清空
        mii.clear();
        
        // 第一个i,没有对应上,要手动设置上
        mii[s1[i]]++;
        mii[s2[i]]--;
        dfs(dfs, i);
        
        // 判断字符种类及其对应个数是否相同
        for(auto t: mii) {
    
    
            if(t.vs != 0) {
    
    
                fg = false;
            }
        }
    }
    puts(fg ? "YES" : "NO");

}

Supongo que te gusta

Origin blog.csdn.net/qq_63432403/article/details/132712062
Recomendado
Clasificación