zoukankan      html  css  js  c++  java
  • hihocoder-第六十一周 Combination Lock

    时间限制:10000ms
    单点时限:1000ms
    内存限制:256MB

    描述

    Finally, you come to the interview room. You know that a Microsoft interviewer is in the room though the door is locked. There is a combination lock on the door. There are N rotators on the lock, each consists of 26 alphabetic characters, namely, 'A'-'Z'. You need to unlock the door to meet the interviewer inside. There is a note besides the lock, which shows the steps to unlock it.

    Note: There are M steps totally; each step is one of the four kinds of operations shown below:

    Type1: CMD 1 i j X: (i and j are integers, 1 <= i <= j <= N; X is a character, within 'A'-'Z')

    This is a sequence operation: turn the ith to the jth rotators to character X (the left most rotator is defined as the 1st rotator)

    For example: ABCDEFG => CMD 1 2 3 Z => AZZDEFG

    Type2: CMD 2 i j K: (i, j, and K are all integers, 1 <= i <= j <= N)

    This is a sequence operation: turn the ith to the jth rotators up K times ( if character A is turned up once, it is B; if Z is turned up once, it is A now. )

    For example: ABCDEFG => CMD 2 2 3 1 => ACDDEFG

    Type3: CMD 3 K: (K is an integer, 1 <= K <= N)

    This is a concatenation operation: move the K leftmost rotators to the rightmost end.

    For example: ABCDEFG => CMD 3 3 => DEFGABC

    Type4: CMD 4 i j(i, j are integers, 1 <= i <= j <= N):

    This is a recursive operation, which means:

    If i > j:
    	Do Nothing
    Else:
    	CMD 4 i+1 j
    	CMD 2 i j 1

    For example: ABCDEFG => CMD 4 2 3 => ACEDEFG

    输入

    1st line:  2 integers, N, M ( 1 <= N <= 50000, 1 <= M <= 50000 )

    2nd line: a string of N characters, standing for the original status of the lock.

    3rd ~ (3+M-1)th lines: each line contains a string, representing one step.

    输出

    One line of N characters, showing the final status of the lock.

    提示

    Come on! You need to do these operations as fast as possible.

    样例输入
    7 4
    ABCDEFG
    CMD 1 2 5 C
    CMD 2 3 7 4
    CMD 3 3
    CMD 4 1 7
    样例输出
    HIMOFIN

    题目分析:

    题意分析

    给定一个字符串s,以及对该字符串s的 m 个操作。

    字符串s包含n个字符,下标为1..n。字符由'A'到'Z'构成,字符增加1表示该字符变为后续字符,比如'A'增加1是'B''C'增加1是'D'。需要注意的是'Z'增加1是'A'

    m个操作包含以下四种类型:

    1. 将字符串第i位到第j位设定为C

      比如当i=2,j=3,C='Z'时:"ABCDEFG"变成"AZZDEFG"

    2. 将字符串第i位到第j位增加K

      比如i=2,j=3,K=1时:"ABCDEFG"变成"ACDDEFG"

    3. 将字符串左边K位移至右边。

      比如K=3时:"ABCDEFG"变成"DEFGABC"

    4. 从字符串第i位到第j位,依次增加1,2,...,j-i+1。

      比如当i=2,j=3时:"ABCDEFG"变成"ACEDEFG"

    输出m个操作结束后的字符串s

    算法分析

    本题需要根据每一次的操作去修改现在的s。若采用朴素的做法,每一次修改其最大代价为O(n),故总的时间复杂度为O(nm)。对于n=50000,m=50000的数据量来说,这样时间复杂度显然是不能够接受的。

    仔细观察我们每一次的操作,其中CMD3是对整体进行了平移,CMD1,CMD2,CMD4都是针对i到j的一个区间进行操作。

    首先我们来解决看似比较简单的CMD3操作:

    若将整个字符串s看作环形,则线型的字符串是从起点指针SP开始顺时针将n个元素进行展开得到的。那么CMD3操作为顺时针移动该环的头指针。举个例子来说:

    CMD3

    最开始头指针在1时,我们展开字符串为[1,2,3,4,5]。当执行CMD3 K=2操作后,起点指针SP移动到3的位置,此时展开的字符串为[3,4,5,1,2]。

    符合CMD3操作的规则,并且起点指针SP的改变就是增加了K。

    其中新字符串的第i~j位,对应的是原字符串第i+SP~j+SP位。

    所以我们只需要维护一个SP指针,当执行CMD3操作时,改变SP的值。而对于其他操作的区间,只需要将区间从[i..j]变化到[i+SP..j+SP]即可。

    需要注意的是,SP,i+SP,j+SP有可能会超过n。当超过n时,需要将其值减去n。

    至此执行CMD3操作的时间复杂度降至O(1)。

    接下来考虑CMD1,CMD2,CMD4。这三个操作均为区间上的操作,因此我们可以使用线段树来进行模拟。(在我们的Hiho一下第19期第20期可以找到线段树的教程)

    在那之前,我们需要对字符进行处理。从题目中我们知道当一个字符超过'Z'时,会直接变成'A'。所以我们可以直接考虑将'A'~'Z'与0~25对应起来。当一个字符增加了很多次K后,其实际表示的字符也就等于该值 mod 26。

    构造线段树

    构造线段树,主要是构造每个节点的数据域,使其能够记录我们需要的信息,同时在父节点和子节点之间能够进行信息的传递。根据本题的题意,我们构造的线段树其节点包含以下三个数据:

    • same: 表示当前区间的字符是否相同,若相同则same等于该字符,否则same=-1
    • add: 表示当前区间的增量,对应CMD2操作所增加的K
    • delta和 inc : 这两个变量是一组,其表示CMD4的操作。其含义为,该区间最左起第1个元素值增量为delta,此后每一个元素的增量比前一个多inc。即第2个元素的增量为delta+inc,第3个元素的增量为delta+inc+inc,...,第i个元素的增量为delta+inc*(i-1)。举个例子:

      若我们对区间[1,3]进行了CMD4操作,实际的意义为s1+1,s[2]+2,s[3]+3。对于表示区间[1,3]的节点,其Delta=1,inc=1。

      若我们对区间[1,3]进行了2次CMD4操作,实际意义为s1+2,s[2]+4,s[3]+6。则此时Delta=2,inc=2。而对于表示区间[2,3]的节点,其Delta=4,inc=2。因为该区间左起第1个元素为s[2]+4,故delta=4。

    在本题中我们一开始便读入了字符串,该字符串的每一个字符对应了树的一个叶子节点。故我们一开始就需要建出整颗树,其代码:

    // 该段代码我们采用的是数组模拟线段树
    const int MAXN = 50001;
    
    struct sTreeNode {
        int left, right;
        int same, add;
        int delta, inc;
        int lch, rch;
    } tree[ MAXN << 2 ];
    
    void createTree(int rt, int left, int right) {
        tree[rt].left = left, tree[rt].right = right;
        tree[rt].delta = tree[rt].step = 0;
        tree[rt].add = 0;
    
        if (left == right) {    // 叶子节点
            tree[rt].base = str[ left ] - 'A';
            tree[rt].lch = tree[rt].rch = 0;
            return ;
        }
    
        // 非叶子节点
        tree[rt].base = -1;
        tree[rt].lch = rt * 2, tree[rt].rch = rt * 2 + 1;
    
        int mid = (tree[rt].left + tree[rt].right) >> 1;
        createTree(tree[rt].lch, left, mid);
        createTree(tree[rt].rch, mid + 1, right);
        return ;
    }

    更新线段树

    在更新线段树时,需要注意更新区间可能会出现i+SP <= n并且j+SP大于n时,此时要将区间分为[i+SP..n]和[1..j+SP-n]两个部分单独处理。

    更新线段树信息的update函数:

    // rt表示当前节点
    // left,right表示此次操作的区间
    // key表示此次操作K或Delta
    // type表示此次操作的类型
    void update(int rt, int left, int right, int key, int type) {
        if (!rt) return ;
        if (tree[rt].right < left || tree[rt].left > right) return ;
        if (left <= tree[rt].left && tree[rt].right <= right) {
            // 当前节点区间完全包含于[left,right]
            // 更新当前区间信息
            ...
        }  else {
            // 当前节点区间不完全包含于[left,right],则需要让子区间来处理
            // 传递当前区间的信息
            ...            
    
            // 更新当前区间信息
            ...
    
            // 迭代处理
            update(tree[rt].lch, left, right, key, type);
            update(tree[rt].rch, left, right, key, type);
        }
        return ;
    }

    若当前区间包含于[left,right],根据操作的不同我们进行如下的处理:

    • CMD1: 直接更新区间的same值,同时将add,delta和inc置为0
      
      if (type == 1) {
          tree[rt].same = key;
          tree[rt].delta = 0, tree[rt].inc = 0;
          tree[rt].add = 0;
      }
    • CMD2: 累加到当前区间的add上
      
      if (type == 2) {
          tree[rt].add += key;
      }
    • CMD4: 将新的delta和inc累加到当前区间的delta和inc上
      
      if (type == 4) {
          tree[rt].delta += key + (tree[rt].left - left);
          tree[rt].inc ++;
      }

    当需要对子区间进行处理时,我们需要将当前区间的信息传递下去,此时需要判断当前区间的same值:

    // 传递当前区间的信息
    int mid = (tree[rt].left + tree[rt].right) / 2;
    
    if (tree[rt].base == -1) {
    // lch
        tree[ tree[rt].lch ].delta += tree[rt].delta;
        tree[ tree[rt].lch ].step  += tree[rt].step;
        tree[ tree[rt].lch ].add   += tree[rt].add;
        // rch
        tree[ tree[rt].rch ].delta += tree[rt].delta + (mid - tree[rt].left + 1) * tree[rt].step;
        tree[ tree[rt].rch ].step  += tree[rt].step;
        tree[ tree[rt].rch ].add   += tree[rt].add;
    }   else {
        tree[ tree[rt].lch ].base = tree[ tree[rt].rch ].base = tree[rt].base;
        tree[ tree[rt].lch ].delta = tree[rt].delta;
        tree[ tree[rt].rch ].delta = tree[rt].delta + (mid - tree[rt].left + 1) * tree[rt].step;
        tree[ tree[rt].lch ].step = tree[ tree[rt].rch ].step = tree[rt].step;
        tree[ tree[rt].lch ].add  = tree[ tree[rt].rch ].add  = tree[rt].add;
    }

    当我们把当前区间的信息传递下去后,可以知道当前区间内的字符一定会发生改变,所以设置其same=1。同时由于当前区间的add,delta和inc信息已经传递下去,其本身的add,delta和inc设置为0:

    // 更新当前区间信息
    tree[rt].base = -1;
    tree[rt].delta = tree[rt].step = 0;
    tree[rt].add = 0;

    产生新的字符串

    在这一步我们需要对整个线段树进行一次遍历,将所有的信息传递到叶子节点,再根据叶子节点的值产生我们新的字符串。

    int f[ MAXN ];    // 记录每个叶子节点的数值
    void getResult(int rt) {
        if (!rt) return ;
        if (tree[rt].base != -1) {
            int delta = tree[rt].delta;
            for (int i = tree[rt].left; i <= tree[rt].right; ++i)
                f[i] = tree[rt].base + tree[rt].add + delta, delta += tree[rt].step;
        } else {
            int mid = (tree[rt].left + tree[rt].right) / 2;
            // lch
            tree[ tree[rt].lch ].delta += tree[rt].delta;
            tree[ tree[rt].lch ].step  += tree[rt].step;
            tree[ tree[rt].lch ].add   += tree[rt].add;
            // rch
            tree[ tree[rt].rch ].delta += tree[rt].delta + (mid - tree[rt].left + 1) * tree[rt].step;
            tree[ tree[rt].rch ].step  += tree[rt].step;
            tree[ tree[rt].rch ].add   += tree[rt].add;
    
            getResult(tree[rt].lch);
            getResult(tree[rt].rch);
        }
        return ;
    }

    此时得到的s并不是我们最后的结果,还需要根据SP的值来输出

    void typeAns() {
        for (int i = 0; i < n; ++i)
            printf("%c", (char) (f[(SP + i) % n] + 'A'));
        printf("
    ");
        return ;
    }
    #include <iostream>
    #include <algorithm>
    #include <string.h>
    #include <stdio.h>
    #include <cmath>
    #include <queue>
    #include <map>
    #define maxn 50000 + 100
    using namespace std;
    string ch;
    char alpha[30] = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    };
    void dfs(int u, int v, int tt)
    {
        int res = 1;
        for(int i = u - 1; i <= v - 1; i++)
        {
            int tt = (ch[i] - 'A' + res) % 26;
            ch[i] = alpha[tt];
            res++;
        }
        return;
    }
    int main()
    {
        int n, m;
        scanf("%d %d", &n, &m);
        cin >> ch;
        int op;
        int u, v, w;
        char c[10];
        char cc[10];
        while(m--)
        {
            cin >> cc >> op;
            if(op == 1)
            {
                scanf("%d %d %s", &u, &v, c);
                for(int i = u - 1; i <= v - 1; i++)
                {
                    ch[i] = c[0];
                }
                //cout << ch << endl;
            }
            else if(op == 2)
            {
                scanf("%d %d %d", &u, &v, &w);
                w = w % 26;
                for(int i = u - 1; i <= v - 1; i++)
                {
                    int tt = (ch[i] - 'A' + w) % 26;
                    ch[i] = alpha[tt];
                }
                // cout << ch << endl;
            }
            else if(op == 3)
            {
                scanf("%d", &w);
                string ch1 = ch;
                string s1 = ch.substr(0, w);
                string s2 = ch1.substr(w, n - w + 1);
                ch = "";
                ch = s2 + s1;
                //cout << ch << endl;
            }
            else if(op == 4)
            {
                scanf("%d %d", &u, &v);
                //printf("%d %d %d
    ", u, v, w);
                dfs(u, v, w);
                // cout << ch << endl;
            }
        }
        cout << ch << endl;
        return 0;
    }
    TLE暴力了一次

    根据题意线段树:

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <string.h>
    #include <stdio.h>
    #include <queue>
    using namespace std;
    #define maxn 50000 + 100
    int len;
    char alpha[30] = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    };
    int sp = 0;
    using namespace std;
    char ch[maxn];
    struct Tire
    {
        int same; ///表示当前区间的字符是否相同,若相同则same等于该字符,否则same=-1
        int add;   ///表示当前区间的增量,对应CMD2操作所增加的K
        int delta; ///该区间最左起第1个元素值增量为delta
        int inc;    ///每一个元素的增量比前一个多
        int val;    ///当前的值
    } tree[maxn << 2];
    void build(int left, int right, int root)
    {
        tree[root].same = -1;
        tree[root].add = 0;
        tree[root].delta = 0;
        tree[root].inc = 0;
        tree[root].val = 0;
        if(left == right)
        {
            return;
        }
        int mid = (left + right) >> 1;
        build(left, mid, root << 1);
        build(mid + 1, right, root << 1 | 1);
        return;
    }
    void change(int& x, int y) {
        x += y;
        x %= 26;
    }
    
    void getid(int s, int e, int& s1, int& e1, int& s2, int& e2) {
        s--;
        e--;
        s1 = s2 = e1 = e2 = -2;
        int t1 = sp + s, t2 = sp + e;
        if(t1 < len && t2 < len) {
            s1 = t1;
            e1 = t2;
            s2 = e2 = -2;
        }
        else if(t1 < len && t2 >= len) {
            t2 %= len;
            s1 = t1;
            e1 = len - 1;
            s2 = 0;
            e2 = t2;
        }
        else if(t1 >= len && t2 >= len) {
            t1 %= len;
            t2 %= len;
            s1 = t1;
            e1 = t2;
            s2 = e2 = -2;
        }
        s1++;
        e1++;
        s2++;
        e2++;
    }
    void pushup(int left, int right, int root)
    {
        if(left == right)
        {
            return;
        }
        if(tree[root].same >= 0)
        {
            tree[root << 1].same = tree[root << 1].val = tree[root].same;
            tree[root << 1 | 1].same = tree[root << 1 | 1].val = tree[root].same;
            tree[root].same = -1;
            tree[root << 1].add = tree[root << 1].delta = tree[root << 1].inc = 0;
            tree[root << 1 | 1].add = tree[root << 1 | 1].delta = tree[root << 1 | 1].inc = 0;
        }
        if(tree[root].add >= 0)
        {
            change(tree[root << 1].add, tree[root].add);
            change(tree[root << 1 | 1].add, tree[root].add);
            tree[root].add = 0;
        }
        if(tree[root].delta >= 0)
        {
            change(tree[root << 1].delta, tree[root].delta);
            change(tree[root << 1].inc, tree[root].inc);
            change(tree[root << 1 | 1].delta, tree[root].delta + ((right - left) / 2 + 1)*tree[root]. inc);
            change(tree[root << 1 | 1].inc, tree[root].inc);
            tree[root].delta = 0;
            tree[root].inc = 0;
        }
        return;
    }
    
    void update(int op, int L, int R, int root, int left, int right, int val)
    {
        pushup(left, right, root);
        if(L <= left && right <= R)
        {
            if(op == 2)
            {
                change(tree[root].add, val);
            }
            else if(op == 4)
            {
                change(tree[root].inc, 1);
                change(tree[root].delta,  left - L  + val);
            }
            else if(op == 1)
            {
                tree[root].same = val;
                tree[root].val = val;
                tree[root].add = 0;
                tree[root].delta = 0;
                tree[root].inc = 0;
            }
            return;
        }
        int mid = (left + right) / 2;
        if(mid >= L) {
            update(op, L, R, root << 1, left, mid, val);
        }
        if(R > mid) {
            update(op, L, R, root << 1 | 1, mid + 1, right, val);
        }
        return;
    }
    int  query(int L, int R, int root, int left, int right)
    {
        pushup(left, right, root);
        if(L <= left && R >= right)
        {
            return (tree[root].val + tree[root].add + tree[root].delta) % 26;
        }
        int mid = (left + right) >> 1;
        if(L <= mid) {
            return  query(L, R, root << 1, left, mid);
        }
        if(R > mid) {
            return  query(L, R, root << 1 | 1, mid + 1, right);
        }
        return 0;
    }
    int main()
    {
        int n, m;
        scanf("%d %d", &n, &m);
        cin >> ch;
        build(1, n, 1);
        for(int i = 0; i < n; i++)
        {
            update(1, i + 1, i + 1, 1, 1, n, ch[i] - 'A');
        }
        sp = 0;
        int op;
        int u, v, w;
        char c[10];
        char cc[10];
        int s1, e1, s2, e2;
        len = n;
        for(int jj = 0; jj < m; jj++)
        {
            cin >> cc >> op;
            if(op == 1)
            {
                cin >> u >> v >> c;
                getid(u, v, s1, e1, s2, e2);
                update(1, s1, e1, 1, 1, n, c[0] - 'A');
                if(s2 != -1 && e2 != -1) {
                    update(1, s2, e2, 1, 1, n, c[0] - 'A');
                }
            }
            else if(op == 2)
            {
                cin >> u >> v >> w;
                getid(u, v, s1, e1, s2, e2);
                update(2, s1, e1, 1, 1, n, w);
                if(s2 != -1 && e2 != -1) {
                    update(2, s2, e2, 1, 1, n, w);
                }
            }
            else if(op == 3)
            {
                scanf("%d", &w);
                sp = sp + w;
                sp = sp % n;
            }
            else if(op == 4)
            {
                cin >> u >> v;
                getid(u, v, s1, e1, s2, e2);
                update(4, s1, e1, 1, 1, n, 1);
                if(s2 != -1 && e2 != -1)
                {
                    update(4, s2, e2, 1, 1, n, e1 - s1 + 2);
                }
            }
        }
        int  tt = sp;
        for(int i = 0; i < n; i++)
        {
            printf("%c", 'A' + query(tt + 1, tt + 1, 1, 1, n));
            tt++;
            tt %= n;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    CSU 1333 Funny Car Racing
    FZU 2195 检查站点
    FZU 2193 So Hard
    ZOJ 1655 FZU 1125 Transport Goods
    zoj 2750 Idiomatic Phrases Game
    hdu 1874 畅通工程续
    hdu 2489 Minimal Ratio Tree
    hdu 3398 String
    洛谷 P2158 [SDOI2008]仪仗队 解题报告
    POJ 1958 Strange Towers of Hanoi 解题报告
  • 原文地址:https://www.cnblogs.com/chenyang920/p/4782289.html
Copyright © 2011-2022 走看看