您的位置:首页 > 其它

luogu P3800 Power收集

2017-06-25 20:16 176 查看
二次联通门 : luogu P3800 Power收集

/*
luogu P3800 Power收集

一眼看出dp方程

dp[i][j] = dp[i][k] + map[i][j] k∈ [j - T, j + T];

一眼知道是单纯dp过不了
考虑优化
之前都是用线段树水
这次明显不行了
N * M *log
再带上大常数。。。

所以用单调队列优化
单调队列维护滑动区间最大值

*/
#include <cstdio>

#define Max 4009

void read (int &now)
{
now = 0;
register char word = getchar ();
while (word < '0' || word > '9')
word = getchar ();
while (word >= '0' && word <= '9')
{
now = now * 10 + word - '0';
word = getchar ();
}
}

int map[Max][Max];
int dp[Max][Max];

int N, M, K, T;
inline int min (int a, int b)
{
return a < b ? a : b;
}

inline int max (int a, int b)
{
return a > b ? a : b;
}

struct Queue_Data
{
int __queue[Max];

int Head, Tail;

void Clear ()
{
for (int i = 1; i <= Tail; i ++)
__queue[i] = 0;

Head = 1;
Tail = 0;
}

inline void Push (int x)
{
__queue[++ Tail] = x;
}

inline void Pop (int i, int pos)
{
for (; Head <= Tail && dp[i - 1][pos] >= dp[i - 1][this->back ()]; Tail --);
}

inline int front ()
{
return __queue[Head];
}

inline int back ()
{
return __queue[Tail];
}
};

Queue_Data Queue;

int main (int argc, char *argv[])
{

read (N);
read (M);
read (K);
read (T);

for (int x, y, z; K --; map[x][y] = z)
{
read (x);
read (y);
read (z);
}

for (int i = 1; i <= M; i ++)
dp[1][i] = map[1][i];

register int R = min (T + 1, M);

for (register int i = 2; i <= N; i ++)
{
Queue.Clear ();

for (register int j = 1; j <= R; j ++)
{
Queue.Pop (i, j);
Queue.Push (j);
}

for (register int j = 1; j <= M; j ++)
{
if (j + T <= M)
{
Queue.Pop (i, j + T);
Queue.Push (j + T);
}

if (Queue.front () < j - T)
Queue.Head ++;
dp[i][j] = dp[i - 1][Queue.front ()] + map[i][j];

}
}
int Answer = 0;

for (int i = 1; i <= N; i ++)
Answer = max (Answer, dp
[i]);

printf ("%d", Answer);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: