P3939 数颜色
P3939 数颜色
题意: 给出一个序列, 有两个操作
1.输出 \([l,r]\) 有几个为 \(c\) 的数
2.交换 \(x\) 与 \(x + 1\) 的数
错误日志: 1.写错了一个数组 2.二分 \(vector\) 手写了(下次使用 \(vector\) 尽量避免手写, 需要熟练运用 \(lower\_bound\) 和 \(upper\_bound\))
Solution
首先考虑数据结构, 无奈不会
然后仔细分析题意(其实这是第一步应该做的!!!!警示!), 发现修改操作一次只是要交换相邻两数
然后分类讨论:
1.相邻两数值相同: 这时交换与不交换等价
2.相邻两数值不同: (重点分析的是这个)
对于一种颜色, 我们先建立一个排名
很显然因为交换的两数颜色不同, 所以交换这两数不会改变 交换颜色的 原排名
所以我们建立一个 \(vector<int>col\) , \(col[i][j]\) 表示第 \(i\) 种颜色 排名第 \(j\) 的坐标, 显然此序列单调递增, 且当 \(j\) 为 \(1-col[i][j]\) 的颜色数
故查询时直接按坐标二分查找, 类似前缀和地处理出有几种颜色
因为修改不会改变排名, 进而不会改变单调性, 所以直接交换位置信息即可
在读入时加入一个指针的概念可以将修改的复杂度优化到 \(O(1)\)
故复杂度上限位 \(O(\log n)\)
Code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
typedef long long LL;
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 300019;
int num, na;
int ori[maxn], pos[maxn];
vector<int>col[maxn];//col[i][j]表示第i个颜色出现第j个的坐标
int main(){
num = RD();na = RD();
for(int i = 1;i <= num;i++)col[i].push_back(0);
for(int i = 1;i <= num;i++){
ori[i] = RD();
col[ori[i]].push_back(i);
pos[i] = col[ori[i]].size() - 1;//访问指针
}
for(int i = 1;i <= na;i++){
int cmd = RD();
if(cmd == 1){
int l = RD(), r = RD(), c = RD();//bound查找返回下标
int p1 = lower_bound(col[c].begin(), col[c].end(), l) - col[c].begin() - 1;
//1 - (l - 1)的个数
int p2 = upper_bound(col[c].begin(), col[c].end(), r) - col[c].begin() - 1;
printf("%d\n", p2 - p1);
}
else{
int x = RD();
if(ori[x] == ori[x + 1])continue;
int c1 = ori[x], c2 = ori[x + 1];
int p1 = pos[x], p2 = pos[x + 1];
swap(col[c1][p1], col[c2][p2]);
swap(pos[x], pos[x + 1]);
swap(ori[x], ori[x + 1]);
}
}
return 0;
}