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;
	}
posted @ 2018-08-19 12:33  Tony_Double_Sky  阅读(119)  评论(0编辑  收藏  举报