[CSP-S模拟测试]:统计(树状数组+乱搞)

题目传送门(内部题120)


输入格式

  第一行,两个正整数$n,m$。
  第二行,$n$个正整数$a_1,a_2,...,a_n$,保证$1\leqslant a_i\leqslant n$,可能存在相同值。
  第三行,$m$个正整数$j_1,j_2,...,j_m$,保证$1\leqslant j_k\leqslant n$。


输出格式

  第一行,第一个整数表示操作前的逆序对数量,接下来$m$个整数表示每次操作后的逆序对数量。


样例

样例输入:

6 2
1 3 4 2 6 1
2 3

样例输出:

6 3 1


数据范围与提示

样例解释:

  第一次操作:由$1\ 3\ 4\ 2\ 6\ 1$变为$1\ 1\ 4\ 2\ 6\ 3$。
  第二次操作:由$1\ 1\ 4\ 2\ 6\ 3$变为$1\ 1\ 2\ 3\ 6\ 4$。
  加粗标记的是这次操作影响到的数。

数据范围:

  对于$40\%$的数据,$1\leqslant n,m\leqslant 200$。
  对于$60\%$的数据,$1\leqslant n,m\leqslant 2,000$。
  对于$80\%$的数据,$1\leqslant n,m\leqslant 20,000$。
  对于$100\%$的数据,$1\leqslant n,m\leqslant 200,000,1\leqslant a_i\leqslant n,1\leqslant j_k\leqslant n$。


题解

$\Theta(n\log n)$求逆序对在此不再赘述。

打开大样例,发现很多都是一样的,于是考虑乱搞……

发现,如果在前面某一次操作时动了某个位置,如果现在还动这个位置则对逆序对个数是没有影响的(相当与没动),直接输出上一次的结果即可。

时间复杂度:$\Theta(n^2\log n)$。

期望得分:$60$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[200001],top;
int e[200001],b[200001];
int tr[200010];
bool vis[200001];
long long ans;
int lowbit(int x){return x&-x;}
void add(int x){for(int i=x;i;i-=lowbit(i))tr[i]++;}
int ask(int x){int res=0;for(int i=x;i<=n+1;i+=lowbit(i))res+=tr[i];return res;}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){ans+=ask(a[i]+1);add(a[i]);}
	printf("%lld ",ans);
	while(m--)
	{
		int k;
		scanf("%d",&k);
		if(vis[k]){printf("%lld ",ans);continue;}
		if(!ans){puts("0");continue;}
		ans=top=0;
		for(int i=1;i<=n;i++)tr[i]=0;
		for(int i=k;i<=n;i++)
			if(a[i]<=a[k])
			{
				vis[i]=1;
				e[++top]=a[i];
				b[top]=i;
			}
		sort(e+1,e+top+1);
		for(int i=1;i<=top;i++)a[b[i]]=e[i];
		for(int i=1;i<=n;i++){ans+=ask(a[i]+1);add(a[i]);}
		printf("%lld ",ans);
	}
	return 0;
}

rp++

猜你喜欢

转载自www.cnblogs.com/wzc521/p/11796134.html