NC222381. 音游家的谱面(Hardversion)
描述
(本题和Easy version的区别仅在数据范围不同)
游玩这类音游时,一般人会使用两只手,或者两只拇指,并且游戏开始时左手放在1号轨道,右手放在第号轨道。在第毫秒中(第毫秒中指这段时间区间,左闭右开),如果两只手分别放在和这两个位置上,那么理论上就可以命中在第i毫秒中轨道和上到达判定线的所有音符。(当然,如果你技术够好的话)
在每一毫秒末,两只手可以独立的各自进行移动,但是只能移动到它们相邻的轨道上。具体来讲,假设左右手在第毫秒中位于和这两个位置上,记为。那么在第毫秒末,共有9种不同的移动方案,即。
在音游中,有一些谱面由于过度反人类,不适合人类游玩而被称为是“粪谱”。
具体来讲,如果假设一开始左手在第号轨道,右手在第号轨道,双手各自独立的按照上述规则,每毫秒末移动一个单位。不存在任何一种方案可以在理论上全部命中的个音符,就称这个谱面是一个“粪谱”。
输入描述
第一行输入两个正整数,表示音游的轨道数目和音符数。
接下来一行个正整数,表示这个音符按照时间顺序出现的轨道编号。
输出描述
输出一行,表示每个音符到达判定线的时间向下取整后的结果。
示例1
输入:
10 10 1 2 3 4 5 6 7 8 9 10
输出:
0 1 2 3 4 4 5 6 7 8
说明:
示例2
输入:
1000 6 1 1000 1 1000 1 1000
输出:
0 0 0 0 0 0
说明:
示例3
输入:
4 10 1 4 2 3 4 1 3 2 1 4
输出:
0 0 1 1 2 2 3 3 4 4
说明:
注意题目要求,要保证是非“粪谱”并且音符到达判定线的时间满足。示例4
输入:
100 2 98 10
输出:
9 9
说明:
答案不唯一C++ 解法, 执行用时: 371ms, 内存消耗: 197048K, 提交时间: 2021-06-27 23:49:10
#include <bits/stdc++.h> using namespace std; #define fi first #define se second const int inf = 0x3f3f3f3f; const int maxn = 5e3+10; int n,m,f[maxn][maxn],a[maxn],pre[maxn][maxn],l[maxn],r[maxn]; typedef pair<int,int>p; p fl[maxn],fr[maxn],q[maxn]; void dfs(int fir,int sec) { if( fir==0 ) return; dfs( fir-1,pre[fir][sec] ); cout << f[fir][sec] << " "; } void solve() { int ans = inf, chu = 0; for(int i=1;i<=n;i++) { if( f[m][i]<ans ) ans = f[m][i],chu = i; if( f[m][i]<ans ) ans = f[m][i],chu = i; } dfs( m,chu ); } int main() { cin >> n >> m; for(int i=1;i<=m;i++) cin >> a[i]; memset( f,0x3f3f3f3f,sizeof f ); f[0][n] = 0, a[0] = 1; for(int i=1;i<=m;i++) { int lim = abs( a[i]-a[i-1] ); for(int j=1;j<=n;j++) l[j] = max(1,j-lim), r[j] = min( n,j+lim ); int head = 1, tail = 0, z = 1; for(int j=1;j<=n;j++)//上一次一只手为j,另一只手为a[i-1] { //按音符的手去按下一个音符,这是一个定值,只需要求出左右区间长度为lim的中f[i-1][j]的最小值即可 while( z<=r[j] ) { while( tail>=head && f[i-1][z]+lim<=q[tail].fi ) tail--; q[++tail] = p( f[i-1][z]+lim,z ); z++; } while( q[head].se<l[j] ) head++; f[i][j] = q[head].fi, pre[i][j] = q[head].se; } for(int j=0;j<=n+1;j++) fl[j] = fr[j] = p(inf,inf); for(int j=1;j<=n;j++)//可以更新的窗口[]递减 { lim = abs( j-a[i] );//递减 int le = max( 1,a[i-1]-lim ), re = min( n,a[i-1]+lim );//可更新窗口 fl[le] = min( fl[le],p( f[i-1][j]+lim,j ) ); fr[re] = min( fr[re],p( f[i-1][j]+lim,j ) ); } for(int j=1;j<=a[i-1];j++) fl[j] = min( fl[j],fl[j-1] );//往右边max for(int j=n;j>=a[i-1];j--) fr[j] = min( fr[j],fr[j+1] );//往左边取max for(int j=1;j<=a[i-1];j++) if( f[i][j]>fl[j].fi ) f[i][j] = fl[j].fi, pre[i][j] = fl[j].se; for(int j=a[i-1];j<=n;j++) if( f[i][j]>fr[j].fi ) f[i][j] = fr[j].fi, pre[i][j] = fr[j].se; } solve(); }