一,普通暴力模拟(52分)
最简单的做法,直接看代码吧。
#include <bits/stdc++.h>
using namespace std;
//p[i]表示排序后i位置的数在原数组p[i]位置
//np[i]表示原数组i位置的数在排序后的数组np[i]
int a[10010], b[10010], p[10010], np[10010], bp[10010], bnp[10010],n, q;
int main()
{
cin>>n>>q;
for(int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
np[i] = p[i] = i;
}
while(q--)
{
int op, x, v;
scanf("%d%d", &op, &x);
if(op == 1)
{
scanf("%d", &v);
a[x] = v;
}
else
{
memcpy(b, a, sizeof a);
memcpy(bp, p, sizeof p);//提前备份
memcpy(bnp, np, sizeof np);
for(int i = 2; i <= n; i ++)
for(int j = i; j > 1 && a[j] < a[j - 1]; j --)
{
swap(a[j], a[j - 1]);
np[p[j]] = j - 1;//插入排序
np[p[j - 1]] = j;
swap(p[j], p[j - 1]);
}
printf("%d\n", np[x]);
memcpy(a, b, sizeof b);
memcpy(p, bp, sizeof bp);//还原
memcpy(np, bnp, sizeof bnp);
}
}
return 0;
}
二,优化掉归并(76分)
通过观察我们可以发现,在这里耗时的主要是归并排序(时间复杂度可是O(N^2)呢!),所以我们可以不用归并来做第二个操作,直接计算有多少个数字>=它,最后再加一就可以求出师原来 a 的第 x 个元素,也就是 a[x]在排序后的新数组所处的位置。
代码:
#include <bits/stdc++.h>
using namespace std;
int a[10010],n,q;
int main()
{
cin>>n>>q;
for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
while(q--)
{
int op,x,v;
scanf("%d%d", &op, &x);
if(op == 1)
{
scanf("%d", &v);
a[x] = v;
}
else
{
int con = 0;
for(int i = 1; i < x; i++)
if(a[i] <= a[x])
con++;
for(int i = x + 1; i <= n; i++)
if(a[i] < a[x])
con++;
printf("%d\n",con + 1);
}
}
return 0;
}
三,正解
我们要定义一个结构体数组,有两个元素,分别是需要存储的量和它的序号,还需要定义一个b数组,用来存放他们排序后的序号。
初始化
操作二需要把a数组进行排序,可操作二的次数不确定,会超时,所以我们应该把排序的任务放在操作一之后以及输入之后。所干的事情应该是把a数组的元素进行排序,可以用sort函数,并且把a数组的下标存入b数组里。
进行操作
首先进行q次循环,可以用while。每次循环,先输入操作类型,然后判断操作类型是一还是二。
操作一
输入x和v先进行修改的任务,只需将原来a数组的第x个元素改v,再重新进行排序,再把存好的a数组的下标存起来,但排序a数组时不能再使用sort函数了,因为这样会超时,那么应该怎么优化呢,不难看出,a数组的元素中,只有一个修改的数是乱的,其余的数排序都是升序排好的,所以我们可以使用两个for循环,一个正着遍历一遍,另一个反着遍历一遍,如果这个数字比前面一个数小,则反着遍历的for可以把它存到适当的位置;反之,如果这个数字比后一个数还要大,则正序遍历的for可以把它存到适当的位置。而这两个for是顺序关系,时间复杂度:O(2n)
操作二
如果是操作二的话,只需要输出b数组里的下标就行了。
代码:
#include <bits/stdc++.h>
using namespace std;
struct dot//建立结构体数组,把数值和编号封装起来
{
int num,id;//num:数值 id:位置
} a[1000001];
bool cmp(dot a,dot b)//用sort给结构体数组排序(数值相等会按编号排序)
{
if(a.num != b.num) return a.num < b.num;
return a.id < b.id;
}
int n,q,b[100001],op,x,v;
int main()
{
scanf("%d%d",&n,&q);
for(int i = 1; i <= n; i++)
{
cin>>a[i].num;
a[i].id = i; //a数组id初始化
}
sort(a + 1,a + n + 1,cmp);
for(int i = 1; i <= n; i++) b[a[i].id] = i; //b数组用来存排序后各数值在排序前的位置
while(q--)
{
scanf("%d",&op);
if(op == 1)
{
scanf("%d%d",&x,&v);
if(a[b[x]].num < v)//v比原来的a[x]的值大,则向后移动,使数组有序
{
a[b[x]].num = v;//赋值
for(int i = b[x]; i < n; i++)//从b[x]向后移动
if(cmp(a[i + 1],a[i]))
swap(a[i],a[i + 1]);
else break;//说明数组已经有序了
}
else//v比原来的a[x]的值小,则向前移动,使数组有序
{
a[b[x]].num = v;//赋值
for(int i = b[x] - 1; i > 0; i--)//从b[x] - 1向前移动
if(cmp(a[i + 1],a[i]))
swap(a[i],a[i + 1]);
else break;//说明数组已经有序了
}
for(int i = 1; i <= n; i++) b[a[i].id] = i; //更新b数组
}
else
{
scanf("%d",&x);
printf("%d\n",b[x]);//这样第二个操作就可以直接O(1)解决了
}
}
return 0;
}