題目鏈接
這題賽後看了一下,挺水的,十分鐘出思路。然後自己傻逼了,竟然套了一個線段樹去寫,強行提高時間複雜度,其實差分一下就好了。
題意:
- 定義這樣一個排列 pi(n),表示將1 ~ n 這個排列的 第 i 個元素放到序列最前面,形成一個新的排列。
- 定義pos(pi(n),3) 表示 3 在這個排列的下標是多少
- 給你一個數組x
- 定義方程
- 然後求f(p1(n)),f(p2(n)),f(p3(n))。。。
題解:這題比較巧妙地是,將xi 和 xi + 1 看成一個貢獻區間(下面直接設爲L 和 R),分別討論區間對 f(pi(n)) 方程的貢獻。
- i不在區間(L, R)內,那麼將 i 提前並不會影響 pos(pi(n), L) 和 pos(pi(n), R) 的相對位置,所以對區間[1, L-1] ,[R+1, n] 貢獻 R-L
- i 在 L端點上,那麼將i提前對pL(n) 貢獻 R - 1
- i 在 R 端點上,將i提前對pR(n) 貢獻 L
- i 在區間內,將i提前相當於把 L 和 R 拉近一個位置,對區間[L + 1, R - 1]貢獻答案 R - L - 1
其實差分就可以了,下面貼我用線段樹寫的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mid (l + r) / 2
#define ls o * 2
#define rs o * 2 + 1
const int N = 2e5 + 10;
ll s[N * 4], lazy[N * 4];
int pre[N];
void pu(int o, int l, int r){
if(lazy[o]){
lazy[ls] += lazy[o];
lazy[rs] += lazy[o];
s[ls] += 1ll * (mid + 1 - l) * lazy[o];
s[rs] += 1ll * (r - mid) * lazy[o];
lazy[o] = 0;
}
}
ll qu(int o, int l, int r, int pos){
if(l == r)
return s[o];
pu(o, l, r);
if(pos <= mid) return qu(ls, l, mid, pos);
else return qu(rs, mid + 1, r, pos);
}
void up(int o, int l, int r, int ql, int qr, int val){
if(ql > qr) return;
if(ql <= l && qr >= r){
lazy[o] += val;
s[o] += 1ll * (r - l + 1) * val;
return;
}
pu(o, l, r);
if(ql <= mid) up(ls, l, mid, ql, qr, val);
if(qr > mid) up(rs, mid + 1, r, ql, qr, val);
s[o] = s[ls] + s[rs];
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i)
scanf("%d", &pre[i]);
for(int i = 2; i <= m; ++i){
int l = pre[i - 1];
int r = pre[i];
if(l > r) swap(l, r);
if(l == r) continue;
up(1, 1, n, l, l, r - 1);
up(1, 1, n, r, r, l);
up(1, 1, n, l + 1, r - 1, r - l - 1);
up(1, 1, n, 1, l - 1, r - l);
up(1, 1, n, r + 1, n, r - l);
}
for(int i = 1; i <= n; ++i)
printf("%lld ", qu(1, 1, n, i));
}