小美的彩带是由一条长度为 
 的彩带一直无限循环得到的,彩带的每一个位置都有一个颜色,用 
 表示。因此当 
 时, 
 。 
	小美每次会从左往右或从右往左剪一段长度为 
 的彩带,她想知道她每次剪下来的彩带有多少种颜色。
第一行输入两个整数
代表彩带长度、剪彩带次数。
第二行输入
个整数
代表彩带每一个位置的颜色。
此后
行,每行输入一个字符
和一个整数
代表裁剪方向和裁剪长度,其中 '
' 说明从左往右剪, '
' 说明从右往左剪 。
对于每一次裁剪彩带,在一行上输出一个整数代表颜色数量。
6 4 1 1 4 5 1 4 L 2 L 3 R 12 R 1
1 3 3 1
第一次剪彩带,剪下来的是
,有
这
种颜色;
第二次剪彩带,剪下来的是
,有
这
种颜色;
第三次剪彩带,剪下来的是
,有
这
种颜色。
第四次剪彩带,剪下来的是
,有
这一种颜色。
import java.io.*;
import java.util.*;
public class Main {
    //树状数组
    private static int lowBit(int x) {
        return x & -x;
    }
    private static void addOfTreeArray(int[] tree, int index, int val) {
        while (index <= tree.length) {
            tree[index] += val;
            index += lowBit(index);
        }
    }
    private static int queryOfTreeArray(int[] tree, int index) {
        int res = 0;
        while (index > 0) {
            res += tree[index];
            index -= lowBit(index);
        }
        return res;
    }
    private static int queryOfTreeArray(int[] tree, int sIndexInclusive,
                                        int eIndexInclusive) {
        return queryOfTreeArray(tree, eIndexInclusive) - queryOfTreeArray(tree,
                sIndexInclusive - 1);
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        String[] split = br.readLine().split(" ");
        int amtOfColorColumns = Integer.parseInt(split[0]);
        int amtOfCuts = Integer.parseInt(split[1]);
        //读取彩带颜色列表
        int[] colorArray = new int[amtOfColorColumns * 2];
        String[] colorNumStrArray = br.readLine().split(" ");
        for (int i = 0; i < amtOfColorColumns; i++) {
            colorArray[i + amtOfColorColumns] = colorArray[i] = Integer.parseInt(
                                                    colorNumStrArray[i]);
        }
        //确定每次裁剪对应的区间,二维数组,第一维表示裁剪的次序,第二个维度表示左侧开始位置、右侧结束位置、裁剪的次序(因为后续会针对intervalLocOfCuts进行排序,需要记录本次裁剪对应的次序)
        int[][] intervalLocOfCuts = new int[amtOfCuts][3];
        int L = 1, R = 2 * amtOfColorColumns;
        for (int i = 0; i < amtOfCuts; i++) {
            intervalLocOfCuts[i][2] = i;
            String[] cutOps = br.readLine().split(" ");
            int lenOfCut = Integer.parseInt(cutOps[1]);
            String dirOfCut = cutOps[0];
            if (lenOfCut >= amtOfColorColumns) {
                intervalLocOfCuts[i][0] = 1;
                intervalLocOfCuts[i][1] = amtOfColorColumns;
                if ("L".equals(dirOfCut)) {
                    L = (L + lenOfCut) % amtOfColorColumns;
                } else {
                    R = (R - lenOfCut % amtOfColorColumns) % amtOfColorColumns +
                        amtOfColorColumns; //num%n的目的避免R-num出现负数的情况
                }
            } else {
                if ("L".equals(dirOfCut)) {
                    intervalLocOfCuts[i][0] = L;
                    intervalLocOfCuts[i][1] = L + lenOfCut - 1;
                    L = (L + lenOfCut) % amtOfColorColumns;
                } else {
                    intervalLocOfCuts[i][0] = R - lenOfCut + 1;
                    intervalLocOfCuts[i][1] = R;
                    R = (R - lenOfCut) % amtOfColorColumns + amtOfColorColumns;
                }
            }
            if (L == 0) {
                L = amtOfColorColumns;
            }
            if (R == amtOfColorColumns) {
                R = 2 * amtOfColorColumns;
            }
        }
        Arrays.sort(intervalLocOfCuts, Comparator.comparingInt(a -> a[1]));
        //遍历颜色列表,边遍历边计算所包含的裁剪区间的颜色的数量
        int indexOfCut = 0;
        Map<Integer, Integer> lastIndexOfColor = new HashMap<>();;
        int[] disColorAmtOfCut = new int[amtOfCuts];
        int[] treeArray = new int[amtOfColorColumns * 2 +
                                  1];//需要使用1--2n的范围,非纯粹的前序和树状数组,为遍历位置处的处理过(每次遍历仅保留同一颜色最后的位置)的彩带颜色列表前序和
        for (int i = 0; i < 2 * amtOfColorColumns; i++) {
            Integer lastIndex = lastIndexOfColor.get(colorArray[i]);
            if (lastIndex != null && lastIndex != 0) {
                addOfTreeArray(treeArray, lastIndex, -1);
            }
            lastIndexOfColor.put(colorArray[i], i + 1);
            addOfTreeArray(treeArray, i + 1, 1);
            //计算所包含的裁剪区间的颜色数量
            while (indexOfCut < amtOfCuts && intervalLocOfCuts[indexOfCut][1] <= i + 1) {
                disColorAmtOfCut[intervalLocOfCuts[indexOfCut][2]] = queryOfTreeArray(treeArray,
                        intervalLocOfCuts[indexOfCut][0], intervalLocOfCuts[indexOfCut][1]);
                indexOfCut++;
            }
        }
        for (int i = 0; i < amtOfCuts; i++) {
            bw.write(disColorAmtOfCut[i] + "\n");
        }
        bw.flush();
        bw.close();
        br.close();
    }
}
 #include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+3;
int a[maxn*2], sq, ans[maxn];
map<int, int> b; // 离散化
struct query { // 莫队
    int l, r, i;
    bool operator<(const query &x) const
    {
        if (l / sq != x.l / sq)
            return l < x.l;
        if (l / sq & 1)
            return r < x.r;
        return r > x.r;
    }
} c***xn];
int cnt[maxn];
int sum;
inline void add(int i) {
    sum += cnt[a[i]]++ == 0;
}
inline void del(int i) {
    sum -= --cnt[a[i]] == 0;
}
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n, q;
    cin >> n >> q;
    for(int i=0;i<n; ++i) cin >> a[i];
    for(int i=0;i<n; ++i) {
        if(b.find(a[i])==b.end()) {
            b[a[i]] = b.size();
        }
        a[i] = b[a[i]];
    }
    for(int i=0;i<n; ++i) a[i+n]=a[i];
    int li = 0, ri = n - 1;
    for(int qi=0;qi<q;++qi) {
        char cm;
        int len,l,r;
        cin >> cm >> len;
        if(cm=='L') {
            l=li, r=li+len-1;
            li=(li+len)%n;
        }else{
            r=ri, l=ri-len+1;
            ri=(ri-len%n+n)%n;
        }
        int lb=l/n-(l<0), rb=r/n-(r<0);
        // cout << l << " " << r << " " << lb << " " << rb << " : ";
        l=(l%n+n)%n, r=(r%n+n)%n;
        if(abs(lb-rb)>=2) {
            cmd[qi] = {0, n-1, qi};
        } else if(lb==rb) {
            cmd[qi] = {l, r, qi};
        } else {
            cmd[qi] = {l, n+r, qi};
        }
    }
    sq=sqrt(n*2);
    sort(cmd, cmd+q);
    for(int i=0,l=0,r=-1; i<q; ++i) {
        while(l>cmd[i].l) add(--l);
        while(r<cmd[i].r) add(++r);
        while(l<cmd[i].l) del(l++);
        while(r>cmd[i].r) del(r--);
        ans[cmd[i].i] = sum;
    }
    for(int i=0;i<q;++i) {
        cout<<ans[i]<<'\n';
    }
    return 0;
} 解法二:思路来自于高赞“重生之不如不重生”的回答。根据他的思路,补充解释:按右端点排序询问,用 last[i] 数组记录在固定右端点下,每个位置 i 是否有颜色。叠树状数组维护 last 的前缀和,然后对每个询问求区间和即可。 #include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+3;
int a[maxn*2], ans[maxn], n;
struct query {
    int l, r, i;
    bool operator<(const query &x) const {
        return r<x.r;
    }
} c***xn];
map<int, int> b; // 离散化
int t[maxn*2];
map<int, int> last; // 记录每个颜色最后出现的位置
void add(int i, int delta) {
    while (i < maxn*2) {
        t[i] += delta;
        i += i & -i;
    }
}
int query(int i) {
    int res = 0;
    while (i > 0) {
        res += t[i];
        i -= i & -i;
    }
    return res;
}
signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int q;
    cin >> n >> q;
    for(int i=0; i<n; ++i) cin >> a[i];
    // 离散化颜色
    for(int i=0; i<n; ++i) {
        if(b.find(a[i]) == b.end()) {
            b[a[i]] = 1 + b.size();
        }
        a[i] = b[a[i]];
    }
    for(int i=0; i<n; ++i) a[i+n] = a[i];
    int li = 0, ri = n - 1;
    // 处理每个查询,确定对应的区间
    for(int qi=0; qi<q; ++qi) {
        char cm;
        int len, l, r;
        cin >> cm >> len;
        if(cm == 'L') {
            l = li;
            r = li + len - 1;
            li = (li + len) % n;
        } else {
            r = ri;
            l = ri - len + 1;
            ri = (ri - len % n + n) % n;
        }
        int lb = l / n - (l < 0 ? 1 : 0);
        int rb = r / n - (r < 0 ? 1 : 0);
        l = (l % n + n) % n;
        r = (r % n + n) % n;
        if(abs(lb - rb) >= 2) {
            cmd[qi] = {0, n-1, qi};
        } else if(lb == rb) {
            cmd[qi] = {l, r, qi};
        } else {
            cmd[qi] = {l, n + r, qi};
        }
    }
    sort(cmd, cmd + q);
    n *= 2; // 扩展后的数组长度为原长度的两倍
    // 处理每个位置,维护树状数组和最后出现的位置
    for(int i=0, qi=0; i < n; ++i) {
        int color = a[i];
        if(last.count(color)) {
            add(last[color] + 1, -1);
        }
        add(i + 1, 1);
        last[color] = i;
        // 处理所有右端点等于当前i的查询
        while (qi < q && cmd[qi].r == i) {
            int current_l = cmd[qi].l;
            ans[cmd[qi].i] = query(i + 1) - query(current_l);
            qi++;
        }
    }
    // 输出所有查询结果
    for(int i=0; i<q; ++i) {
        cout << ans[i] << '\n';
    }
    return 0;
} package com.e; import java.util.*; /** * 10 10 * 4 5 3 10 4 7 10 5 4 8 * L 8 * R 4 * L 3 * L 4 * L 1 * L 3 * R 4 * R 3 * L 10 * L 10 * 这个案例没通过 * * * 1 5 1 5 * 2 4 2 4 * 3 3 3 2 * 4 4 4 4 * 5 1 5 1 * 6 3 6 3 * 7 3 7 4 * 8 3 8 3 * 9 6 9 6 * 10 6 10 6 */ public class 减彩带 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int q = scanner.nextInt(); int[] colors = new int[n]; Set<Integer> set=new HashSet<>(); for (int i = 0; i < n; i++) { colors[i] = scanner.nextInt(); set.add( colors[i]); } // 初始化裁剪区间 int start = 0; int end = -1; int leftIndex=0; int beforLeftIndex=0; int rightIndex=0; int beforeRightIndex=0; int ColorCount[]=new int[q-1]; for(int j=0;j<q;j++){ String derect=scanner.next(); int cutCount=scanner.nextInt(); if(derect.equals("L")){ if(cutCount>=colors.length){ ColorCount[j]= set.size(); beforLeftIndex=leftIndex; leftIndex=(beforLeftIndex+cutCount)%colors.length; } if(cutCount<colors.length){ beforLeftIndex=leftIndex; leftIndex=(beforLeftIndex+cutCount)%colors.length; ColorCount[j]=getColorSizeLeft(beforLeftIndex,leftIndex,colors); } } //从右到左的下标取值时,要从颜色数组的反向取 if(derect.equals("R")){ if(cutCount>=colors.length){ ColorCount[j]= set.size(); beforeRightIndex=beforeRightIndex; rightIndex=(rightIndex+cutCount)%colors.length; } if(cutCount<colors.length){ beforLeftIndex=leftIndex; leftIndex=(leftIndex+cutCount)%colors.length; ColorCount[j]=getColorSizeRight(beforLeftIndex,leftIndex,colors); } } } for (int i = 0; i < ColorCount.length; i++) { System.out.print(ColorCount[i]+" "); } } public static int getColorSizeLeft(int startIndex,int endIndex,int[] colors){ Set<Integer> set=new HashSet<>(); if(endIndex>startIndex){ for(int i=startIndex;i<endIndex;i++){ set.add(colors[i]); } }else{ for(int i=startIndex;i<colors.length;i++){ set.add(colors[i]); } for(int i=0;i<endIndex;i++){ set.add(colors[i]); } } return set.size(); } public static int getColorSizeRight(int startIndex,int endIndex,int[] colors){ Set<Integer> set=new HashSet<>(); if(endIndex>startIndex){ for(int i=startIndex;i<endIndex;i++){ set.add(colors[(colors.length-1)-i]); } }else{ for(int i=startIndex;i<colors.length;i++){ set.add(colors[colors.length-1-i]); } for(int i=0;i<endIndex;i++){ set.add(colors.length-1-i); } } return set.size(); } }
def solve(color_left, color_right, c, x): color_set = set(color_left) full_color_count = 0 if x >= len(color_left): x = x % len(color_left) full_color_count = len(color_set) if c == "L": cut_arr = color_left[:x] cut_set = set(cut_arr) color_count = len(cut_set) new_left = color_left[x:] new_left.extend(cut_arr) if full_color_count: return new_left, color_right, full_color_count else: return new_left, color_right, color_count elif c == "R": cut_arr = color_right[:x] cut_set = set(cut_arr) color_count = len(cut_set) new_right = color_right[x:] new_right.extend(cut_arr) if full_color_count: return color_left, new_right, full_color_count else: return color_left, new_right, color_count if __name__ == "__main__": n, q = list(map(int, input().split())) color_left = list(map(int, input().split())) color_right = color_left.copy() color_right.reverse() for _ in range(q): c, x = list(input().split()) x = int(x) color_left, color_right, ans = solve(color_left, color_right, c, x) print(ans)