NOI Oline 1 题解

A太简单不写了

B

要知道有个结论
设c[i]为i前面有c[i]个比a[i]大的,显然 ∑ c [ i ] \sum c[i] c[i]就是逆序对的个数
做一次冒泡排序回将所有c[i]不为0的全部减 1
这个很容易证明,随便推一下就好了
假设i前面存在比a[i]大的
那么一定会有一个比a[i]大的和a[i]交换
即c[i] - 1
知道这个结论之后就很容易了

每次询问k的答案就是

A N S = ∑ i = k + 1 n c [ i ] − k ∑ i = k + 1 n [ v i s [ i ] = = 1 ] ANS = \sum\limits_{i=k+1}^nc[i] - k\sum\limits_{i=k+1}^n[vis[i]==1] ANS=i=k+1nc[i]ki=k+1n[vis[i]==1]

拿两个树状数组分别维护一下就好了
code:

#include<bits/stdc++.h>
#define N 1000005
#define int long long
#define lowbit(x) (x & -x)
using namespace std;
int tree1[N + 5], tree2[N + 5];
void update1(int x, int y) {
	if(x)
	for(; x < N; x += lowbit(x)) tree1[x] += y;
}
int query1(int x) {
	int ret = 0;
	for(; x; x -= lowbit(x)) ret += tree1[x];
	return ret;
} 
void update2(int x, int y) {
	if(x)
	for(; x < N; x += lowbit(x)) tree2[x] += y;
}
int query2(int x) {
	int ret = 0;
	for(; x; x -= lowbit(x)) ret += tree2[x]  ;
	return ret;
}
int n, m, a[N], c[N];
signed main() {
	scanf("%lld%lld", &n, &m);
	for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
	for(int i = 1; i <= n; i ++) {
		c[i] = i - 1 - query1(a[i]);
		update1(a[i], 1);
	}
	memset(tree1, 0, sizeof tree1);
	for(int i = 1; i <= n; i ++) update1(c[i], c[i]), update2(c[i], 1);
	while(m --) {
		int opt, x;
		scanf("%lld%lld", &opt, &x);
		if(opt == 1) {
			update1(c[x], -c[x]), update2(c[x], - 1); update1(c[x + 1], -c[x + 1]), update2(c[x + 1], - 1);
			if(a[x] < a[x + 1]) c[x] ++; else c[x + 1] --;
			swap(c[x], c[x + 1]), swap(a[x], a[x + 1]);
			update1(c[x], c[x]), update2(c[x], 1); update1(c[x + 1], c[x + 1]), update2(c[x + 1], 1);
		} else {
			if(x >= n) printf("0\n");
			else {
				int X = query1(N - 1) - query1(x);
				int Y = query2(N - 1) - query2(x);
				printf("%lld\n", X - x * Y);
			}
		}
	}
	return 0;
}

C

如果只有k=1的话就是小学奥数
有一个很显然的结论就是

…, 6, 4, 2, 1, 3, 5, … 这么放是最优的

知道这个结论之后就很简单了
对于k不等于1的可以拆成若干个长度相等的环
然后把原数列排序后按换长度分成一段一段,再按k=1的情况做就好了
直接做会超时,记忆化一下,最多就d(n)种不同的环长
code:

#include<bits/stdc++.h>
#define int long long
#define N 1000005
using namespace std;
int n, m, a[N], vis[N];
signed main() {
	scanf("%lld%lld", &n, &m);
	for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
	sort(a + 1, a + 1 + n);
	while(m --) {
		int k;
		scanf("%lld", &k);
		if(! k) { int ret = 0;
			for(int i = 1; i <= n; i ++)
				ret += a[i] * a[i];
			printf("%lld\n", ret); continue;
		}
		int d = n /  __gcd(k, n); 
		if(vis[d]) printf("%lld\n", vis[d]);
		else {
			for(int i = 1; i <= n; i += d) { int to = i + d - 1;
				for(int j = i; j <= to - 2; j += 2) vis[d] += a[j] * a[j + 2];
				for(int j = i + 1; j <= to - 2; j += 2) vis[d] += a[j] * a[j + 2];
				vis[d] += a[i] * a[i + 1] + a[to] * a[to - 1]; 
			}
			printf("%lld\n", vis[d]);
		}
	}
	return 0;
}

posted @ 2020-05-10 14:28  lahlah  阅读(23)  评论(0编辑  收藏  举报