CF #771 div.2
A. Reverse(思维)
题目大意
给定长度为n的序列 可以翻转任意一段 输出他的最小字典序排列
解题思路
因为要最小字典序 所以从n = 1开始向后枚举 如果说q[i] != i 那就说明把 i 换回这里字典序最小(贪心
很重要的一点是因为我的程序鲁棒性不够强 所以导致了数组越界 达到了负的下标
以后一定要注意在模拟reverse的时候不能越界
代码
int q[510], n;
void solve()
{
cin >> n;
for(int i = 1; i <= n; i++) cin >> q[i];
int j = 1;
while(q[j] == j) j ++;
int k = n;
while(q[k] != j && k > 0) k --;
for(int i = j, l = k; i < l; i ++, l --)
swap(q[i], q[l]);
for(int i = 1; i <= n; i++) cout << q[i] << " ";
cout << endl;
}
B. Odd Swap Sort(构造)
题目大意
给定长度为n的数组 当ai + ai+1是奇数时 你可以对其调换位置 这样的操作不限次数 问是否能够将序列变成单调不降序列(ai <= ai+1)
解题思路
ai + ai+1是奇数 代表我们只能调换不同奇偶性的两个相邻元素
所以可以把数组中的元素分为奇数和偶数两个部分
可以发现,如果奇数数组中或是偶数数组中有逆序的情况,是无法通过题目中的特定操作来把数组变为不严格的升序数组的
代码
int n;
vector<int> o, e;//odd even
void solve()
{
o.clear();e.clear();
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
int x;scanf("%d", &x);
if(x & 1) o.push_back(x);
else e.push_back(x);
}
bool flag = true;
for(int i = 1; i < o.size(); i++)
{
if(o[i] < o[i - 1])
{
flag = false;
break;
}
}
for(int i = 1; i < e.size(); i++)
{
if(e[i] < e[i - 1])
{
flag = false;
break;
}
}
if(flag) puts("YES");
else puts("NO");
}
英语积累:non-decreasing order 单调不降序列
C. Inversion Graph(单调栈)
题目大意
为一个长度为n的序列的逆序对连边(如果后面的数小于前面的数,就将这两个数(包括这两个数所在的连通块), 求连通块的个数
解题思路
逆序对
如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。
如果用朴素的做法 就是双循环 但test = 2e5 显然只能使用nlong或者n的算法
我们来观察一下样例
可以发现后一个连通块中的所有数大于前一个连通块中的所有数
以连通块(也就是集合)的角度来思考,如果一个数小于前面连通块的最大数,则将这个数添加至连通块中,否则,这个数作为新的连通块
选取样例
3 2 1 6 5 4
得到的栈 3 - 3 - 3 - 3,6 - 3,6 - 3,6
此时还未万事大吉
3 1 5 2 4
若是按上面的方法分析 3 - 3 - 3,5 - 3,5 - 3,5
但是 答案应该是1 因为2连边到3和5 3和5应该是在一个连通块中的
得出结论:当一个数属于当前连通块时,还应该继续判断是否属于前面的连通块
代码
void solve()
{
stack<int> stk;// 用单调栈维护每个连通块中最大的元素
cin >> n;
for(int i = 0; i < n; i++)
{
int x;
cin >> x;
if(stk.size() == 0) stk.push(x);
else // stk.size()
{
if(x > stk.top()) stk.push(x);// 大于当前栈顶直接入栈
else // 否则保存栈顶,将栈中大于这个数的元素出栈,然后将原栈顶重新入栈
{
int t = stk.top();
while(stk.size() && t > x) stk.pop();
stk.push(t);
}
}
}
cout << stk.size() << endl;
}