NOI.AC #111. 運氣大戰 動態Dp 線段樹維護矩陣乘法 貪心
阿新 • • 發佈:2019-01-03
NOI.AC #111. 運氣大戰
題目傳送門
分析
如果沒有錯排這個條件,根據排序不等式,肯定直接排序優秀。
有了錯排這個條件,有一個神奇的結論,如果第
個數匹配的是第
個數,那麼
排序之後考慮
如果
和
可以匹配,那麼有
(排序後)
注意到這樣轉移一定是最優的。
否則的話討論一下錯排,轉移類似。
良心出題人沒有卡常,可以過。
資料大一點的話,就要考慮動態
實際上轉移方程式是一個帶
的三階遞推。
用線段樹維護矩陣乘法即可。
複雜度
或
程式碼
暴力改
#include<bits/stdc++.h>
const int N = 3e4 + 10; const long long inf = 1e18;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int n, q, w[N], r[N], c1[N], c2[N], loc[N];
long long cost[N][2], f[N];
bool cmp1(int u, int v) {return w[u] > w[v];}
bool cmp2(int u, int v) {return r[u] > r[v];}
void Mx(long long &a, long long b) {a = std::max(a,b);}
long long Calc(int a, int b) {
if(a <= 0 || b <= 0 || a > n || b > n) return -inf;
return 1LL * w[c1[a]] * r[c2[b]];
}
void Up(int x) {
if(x <= 0 || x > n) return ;
cost[x][0] = Calc(x - 1, x) + Calc(x, x - 1);
cost[x][1] = std::max(Calc(x - 2, x - 1) + Calc(x - 1, x) + Calc(x, x - 2),
Calc(x, x - 1) + Calc(x - 1, x - 2) + Calc(x - 2, x));
}
int main() {
n = ri(); q = ri();
for(int i = 1;i <= n; ++i) w[i] = ri(), c1[i] = i;
for(int i = 1;i <= n; ++i) r[i] = ri(), c2[i] = i;
std::sort(c1 + 1, c1 + n + 1, cmp1);
std::sort(c2 + 1, c2 + n + 1, cmp2);
for(int i = 1;i <= n; ++i) loc[c2[i]] = i, Up(i);
for(;q--;) {
int u = ri(), v = ri();
std::swap(r[u], r[v]); std::swap(loc[u], loc[v]);
c2[loc[u]] = u; c2[loc[v]] = v;
for(int i = 0;i < 3; ++i)
Up(c2[u] + i), Up(c2[v] + i);
for(int i = 1;i <= n; ++i) {
f[i] = 0;
if(i > 1) Mx(f[i], f[i - 2] + cost[i][0]);
if(i > 2) Mx(f[i], f[i - 3] + cost[i][1]);
if(c1[i] != c2[i])
Mx(f[i], f[i - 1] + Calc(i, i));
}
printf("%lld\n", f[n]);
}
return 0;
}
線段樹
#include<bits/stdc++.h>
const int N = 3e4 + 10, Nt = 32768;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int n, q, w[N], r[N], loc[N];
struct D {int p, x;}a[N], b[N];
bool cmp(D a, D b) {return a.x < b.x;}
struct Maxtir {
long long m[3][3];
long long *operator [] (int x) {return m[x];}
Maxtir() {memset(m, -0x3f, sizeof(m)); for(int i = 0;i < 3; ++i) m[i][i] = 0;}
long long Calc(int x, int y) {return 1LL * a[x].x * b[y].x;}
void Init(int x) {
memset(m, -0x3f, sizeof(m));
if(a[x].p != b[x].p) m[0][0] = Calc(x, x);
if(x > 1) m[1][0] = Calc(x - 1, x) + Calc(x, x - 1);
if(x > 2) m[2][0] = std::max(Calc(x - 2, x - 1) + Calc(x - 1, x) + Calc(x, x - 2),
Calc(x, x - 1) + Calc(x - 1, x - 2) + Calc(x - 2, x));
m[0][1] = m[1][2] = 0;
}
Maxtir operator * (Maxtir a) {
Maxtir c; memset(c.m, -0x3f, sizeof(c.m));
for(int i = 0;i < 3; ++i)
for(int j = 0;j < 3; ++j)
for(int k = 0;k < 3; ++k)
c[i][j] = std::max(c[i][j], m[i][k] + a[k][j]);
return c;
}
}T[Nt << 1];
void Up(int i) {
if(i > n) return ;T[i + Nt].Init(i);
for(i += Nt;i >>= 1;) T[i] = T[i << 1] * T[i << 1 | 1];
}
int main() {
n = ri(); q = ri();
for(int i = 1;i <= n; ++i) a[i] = (D){i, ri()};
for(int i = 1;i <= n; ++i) b[i] = (D){i, ri()};
std::sort(a + 1, a + n + 1, cmp);
std::sort(b + 1, b + n + 1, cmp);
for(int i = 1;i <= n; ++i) loc[b[i].p] = i;
for(int i = 1;i <= n; ++i) T[i + Nt].Init(i);
for(int i = Nt - 1; i; --i) T[i] = T[i << 1] * T[i << 1 | 1];
for(;q--;) {
int u = ri(), v = ri();
std::swap(b[loc[u]].p, b[loc[v]].p);
std::swap(loc[u], loc[v]);
for(int i = 0;i < 3; ++i)
Up(loc[u] + i), Up(loc[v] + i);
printf("%lld\n", T[1][0][0]);
}
return 0;
}