其他分享
首页 > 其他分享> > P1070 [NOIP2009 普及组] 道路游戏

P1070 [NOIP2009 普及组] 道路游戏

作者:互联网

道路游戏

给定一个长度为 \(n\) 的环,每条边每个时刻都有对应的价值,经过即得到。

每个时刻如果没有在运动,就可以任意选择起点和持续运动时间,每次运动将移动一条边的长度。

对于选择的起点 \(i\) 需要减去 \(a_i\) 的价值,\(a_i\) 不随时间变化改变。

求最终价值的最大值,有可能为负。

\(f(i)\)​ 表示第 \(i\)​ 个时刻的 \(\max\)​​。\(g(i,j)\)​​ 位置 \(i\)​ 时间 \(j\)​ 斜线上的前缀和。

\(f(i)=\max\limits_{0<j<n,1\leq k\leq p}\{f(i-k)+g(j-1,i)-g(j-k-1,i-k)-a(j-k)\}\)​

这样可以做到 \(O(n^3)\),需要追求优化。​​

设 \(h(i,j)=f(i)-g(j-1,i)-a(j)\)

\(f(i)=\max\limits_{0<j<n,1\leq k\leq p}\{h(i-k,j-k)+g(j-1,i)\}\)

相当于是斜线上的单调队列,至于队列编号的钦定...,不妨定为 \((i-j+1)\mod n\)​。

这样钦定的原因是,第一行的队列编号就是 \(0\sim n-1\),比较优美。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 1010;
const int INF = 1e9;
int n, m, p;
int f[N], a[N], g[N][N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

#define MP make_pair
#define V first
#define P second
typedef pair<int, int> PII;

struct Queue{
	int l, r; PII q[N];
	void Init(){l = 1, r = 0;}
	int Front(){return q[l].V;}
	void Push(int i, int val){
		while(l <= r && q[r].V <= val) r --;
		q[++ r] = MP(val, i);
	}
	void Pop(int limit){
		while(l <= r && q[l].P < limit) l ++;
	}
} Q[N];

int Get(int i, int j){return ((i - j) + 2 * n) % n;}

int main(){
	n = read(), m = read(), p = read();
	for(int i = 0; i < n; i ++)
		for(int j = 1; j <= m; j ++) g[i][j] = read();
	for(int i = 0; i < n; i ++) a[i] = read();

	for(int j = 2; j <= m; j ++)
		for(int i = 0; i < n; i ++)
			g[i][j] += g[Get(i, 1)][j - 1];
	for(int i = 0; i < n; i ++){
		int k = (i == n - 1) ? 0 : i + 1;
		Q[k].Init();
		Q[k].Push(0, - a[i]);
	}

	for(int i = 1; i <= m; i ++) f[i] = - INF;
	for(int i = 1; i <= m; i ++){
		for(int j = 0; j < n; j ++){
			int k = Get(j, i - 1);
			Q[k].Pop(i - p);
			f[i] = max(f[i], Q[k].Front() + g[Get(j, 1)][i]);
		}
		for(int j = 0; j < n; j ++)
			Q[Get(j, i - 1)].Push(i, f[i] - g[Get(j, 1)][i] - a[j]);
	}
	printf("%d\n", f[m]);
	return 0;
}

标签:普及,int,max,NOIP2009,getchar,队列,while,include,P1070
来源: https://www.cnblogs.com/lpf-666/p/15083057.html