NOI Oline 1 題解

A太簡單不寫了

B

要知道有個結論
設c[i]爲i前面有c[i]個比a[i]大的,顯然c[i]\sum c[i]就是逆序對的個數
做一次冒泡排序回將所有c[i]不爲0的全部減 1
這個很容易證明,隨便推一下就好了
假設i前面存在比a[i]大的
那麼一定會有一個比a[i]大的和a[i]交換
即c[i] - 1
知道這個結論之後就很容易了

每次詢問k的答案就是

ANS=i=k+1nc[i]ki=k+1n[vis[i]==1]ANS = \sum\limits_{i=k+1}^nc[i] - k\sum\limits_{i=k+1}^n[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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章