1. 程式人生 > >NOI.AC #111. 運氣大戰 動態Dp 線段樹維護矩陣乘法 貪心

NOI.AC #111. 運氣大戰 動態Dp 線段樹維護矩陣乘法 貪心

NOI.AC #111. 運氣大戰

題目傳送門

分析

如果沒有錯排這個條件,根據排序不等式,肯定直接排序優秀。
有了錯排這個條件,有一個神奇的結論,如果第 i i 個數匹配的是第 j j 個數,那麼

i j 2 |i-j|\le 2
排序之後考慮 D p
Dp

如果 i i j j 可以匹配,那麼有
f
[ i ] = f [ i 1 ] + w [ i ] r [ i ] f[i]=f[i-1]+w[i]\cdot r[i]
(排序後)
注意到這樣轉移一定是最優的。
否則的話討論一下錯排,轉移類似。
良心出題人沒有卡常,可以過。
資料大一點的話,就要考慮動態 D p Dp
實際上轉移方程式是一個帶 M a x Max 的三階遞推。
用線段樹維護矩陣乘法即可。
複雜度 O ( q n ) O(qn) O ( q l o g n ) O(qlogn)

程式碼

暴力改

#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;
}