傳送門
文章目錄
題意:
比如下面這個圖:
思路:
對于這個題,比較容易就能考慮到dpdpdp,設f[i][j]f[i][j]f[i][j]為到了第iii行,覆蓋了[j,j+k?1][j,j+k-1][j,j+k?1]范圍時候的最大值。
先不考慮重疊的情況,那么轉移方程就是f[i][j]=max(f[i?1][t]+∑x=tt+k?1a[i][x])+∑x=jj+k?1a[i][x]f[i][j]=max(f[i-1][t]+\sum _{x=t}^{t+k-1}a[i][x])+\sum _{x=j}^{j+k-1}a[i][x]f[i][j]=max(f[i?1][t]+x=t∑t+k?1?a[i][x])+x=j∑j+k?1?a[i][x]
考慮重疊的情況,將區間[j,j+k?1][j,j+k-1][j,j+k?1]表示為[l,r][l,r][l,r]。
對于easyeasyeasy版本,因為k≤20k\le 20k≤20,所以重疊的部分不會多于202020,所以我們維護一個前后綴的最大值,先取max(pmx[l?1],smx[r+1])max(pmx[l-1],smx[r+1])max(pmx[l?1],smx[r+1]),讓后對于[l?k+1,r][l-k+1,r][l?k+1,r]的部分,我們刪掉其重疊的部份,讓后轉移即可,復雜度O(nmk)O(nmk)O(nmk)。
對于hardhardhard版本,由于k≤mk\le mk≤m,所以上面的復雜度不可行,考慮優化。對于區間滑動問題,我們考慮線段樹或者單調隊列,由于線段樹比較好理解,說一下線段樹的吧。
我們設g[i][j]=f[i][j]+∑x=jj+k?1a[i][j]g[i][j]=f[i][j]+\sum _{x=j}^{j+k-1}a[i][j]g[i][j]=f[i][j]+x=j∑j+k?1?a[i][j]
考慮a[x][y]a[x][y]a[x][y]都在哪里被重復計算了,可以發現在g[x?1][y?k+1],...,g[x?1][y]g[x-1][y-k+1],...,g[x-1][y]g[x?1][y?k+1],...,g[x?1][y]都被重復計算了,我們只需要減去他們,就可以取maxmaxmax加到f[x][y]f[x][y]f[x][y]上了。這個顯然可以用線段樹來實現,但是復雜度還是O(nmk)O(nmk)O(nmk)的,但是我們可以發現,當從jjj到j+1j+1j+1的時候,jjj不再重疊,j+1+k?1j+1+k-1j+1+k?1產生重疊的部分,分別將這兩個地方處理一下就好了。
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std
;
typedef long long LL
;
typedef unsigned long long ULL
;
typedef pair
<int,int> PII
;const int N
=55,M
=20010,mod
=1e9+7,INF
=0x3f3f3f3f;
const double eps
=1e-6;int n
,m
,k
;
int a
[N
][M
],s
[N
][M
],f
[N
][M
];
struct Node
{int l
,r
;int val
,lazy
;
}tr
[M
<<2];void pushup(int u
)
{tr
[u
].val
=max(tr
[L
].val
,tr
[R
].val
);
}void pushdown(int u
)
{int lazy
=tr
[u
].lazy
; tr
[u
].lazy
=0;tr
[L
].val
+=lazy
; tr
[L
].lazy
+=lazy
;tr
[R
].val
+=lazy
; tr
[R
].lazy
+=lazy
;
}void build(int u
,int l
,int r
,int pos
)
{tr
[u
]={l
,r
};if(l
==r
){tr
[u
].val
=f
[pos
][l
]+s
[pos
+1][l
+k
-1]-s
[pos
+1][l
-1];tr
[u
].lazy
=0;return;}build(L
,l
,Mid
,pos
); build(R
,Mid
+1,r
,pos
);pushup(u
);
}void modify(int u
,int l
,int r
,int val
)
{if(tr
[u
].l
>=l
&&tr
[u
].r
<=r
){tr
[u
].val
+=val
;tr
[u
].lazy
+=val
;return;}pushdown(u
);if(l
<=Mid
) modify(L
,l
,r
,val
);if(r
>Mid
) modify(R
,l
,r
,val
);pushup(u
);
}void add(int val
,int pos
)
{if(pos
>=1) modify(1,max(1,pos
-k
+1),min(pos
,m
-k
+1),-a
[val
][pos
]);
}void del(int val
,int pos
)
{if(pos
>=1) modify(1,max(1,pos
-k
+1),min(pos
,m
-k
+1),a
[val
][pos
]);
}int main()
{
int ans
=0;scanf("%d%d%d",&n
,&m
,&k
);for(int i
=1;i
<=n
;i
++) for(int j
=1;j
<=m
;j
++) scanf("%d",&a
[i
][j
]),s
[i
][j
]=s
[i
][j
-1]+a
[i
][j
];for(int i
=1;i
<=n
;i
++){if(i
>1) for(int j
=1;j
<k
;j
++) add(i
,j
);for(int j
=1;j
<=m
-k
+1;j
++){int l
=j
,r
=j
+k
-1;f
[i
][j
]=s
[i
][r
]-s
[i
][l
-1];if(i
!=1) del(i
,l
-1),add(i
,r
),f
[i
][j
]+=tr
[1].val
;}build(1,1,m
-k
+1,i
);}for(int i
=1;i
<=m
;i
++) ans
=max(ans
,f
[n
][i
]);printf("%d\n",ans
);return 0;
}
總結
以上是生活随笔為你收集整理的Codeforces Round #620 (Div. 2) F2. Animal Observation (hard version) dp + 线段树的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。