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+1∑nc[i]−ki=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;
}