思路:
本質上能進行的操作就是我們算出從第 \(i\) 塊磚開始,連續刷 \(M\) 塊磚,是否有承包商可以刷出期望顏色。
那麼設 \(f_i\) 表示 \([i,i+m-1]\) 是否合法,那麼就變成了最小區間覆蓋問題。
最小區間覆蓋問題
令 \(Max\) 表示當前覆蓋了 \([1,Max]\)。
那麼我們需要找到左端點在 \([1,Max+1]\) 內,且右端點最大的區間。
在本題中因為區間長度都為 \(m\),那麼我們只需要找到儘可能在最後的合法 \(f_i\)。
此時 \(Max \gets i+m-1\),那麼對於小於 \(j\) 的 \(i\),是無法覆蓋到 \(i+m-1\) 之後的,於是就沒有貢獻了,於是我們可以直接走指標維護。
最小區間覆蓋程式碼
ll get(){
ll Max=-1,x=0,id,ans=0;
while(Max<n-1){
id=-1;
while(x<=Max+1&&x<n){
if(f[x])
id=x;
x++;
}
if(id==-1)
return -1;
Max=id+m-1;
ans++;
}
return ans;
}
那麼我們需要考慮的就是如何求出 \(f_i\)。
28pts:
對於每個 \(i\),暴力列舉一個 \(j\),然後檢視是否能匹配上。
時間複雜度為 \(O(NM^2)\)。
51pts:
考慮動態規劃最佳化,定義 \(dp_{i,j}\) 表示從第 \(i\) 塊牆壁開始,從第 \(j\) 個商家開刷最多能刷幾塊牆。
那麼若第 \(j\) 個商家不能刷第 \(i\) 個牆壁,則:
\[dp_{i,j}=0
\]
否則能刷:
\[dp_{i,j} = dp_{i+1,(j+1) \bmod M} + 1
\]
注意,空間開不下,考慮滾動陣列最佳化。
時間複雜度為 \(O(NM)\)。
震驚的是,這玩意兒竟然過了……
離大譜。
$O(NM)$ 程式碼
int minimumInstructions(int N, int M, int K,vector<int> C,vector<int> A,vector<vector<int>> B){
n=N,m=M,k=K;
for(int i=0;i<n;i++)
a[i]=C[i];
for(int i=0;i<m;i++){
len[i]=A[i];
for(auto v:B[i])
s[v].push_back(i);
}
for(int i=n-1;i>=0;i--){
for(int j=0;j<m;j++)
dp[i&1ll][j]=0;
for(auto j:s[a[i]]){
dp[i&1ll][j]=dp[(i&1ll)^1ll][(j+1)%m]+1;
if(dp[i&1ll][j]>=m)
f[i]=1;
}
}
cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB"<<'\n';
return get();
}
100pts:
注意到可以最佳化上面狀態轉移的 \(j\)。
我們可以提前預處理能刷顏色 \(x\) 的承包商的集合 \(S_x\),那麼 \(j \in S_{c_i}\)。
但是因為是滾動陣列,實時清空的話複雜度又上去了,那麼再維護一個時間戳即可。
時間複雜度為 \(O(\sum f(k))\)。
完整程式碼:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef int ll;
bool Begin;
const ll N=100100,M=50050;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
ll n,m,k;
ll a[N],len[M];
ll dp[2][M],low[2][M];
vector<ll> s[N];
bool f[N];
bool End;
ll get(){
ll Max=-1,x=0,id,ans=0;
while(Max<n-1){
id=-1;
while(x<=Max+1&&x<n){
if(f[x])
id=x;
x++;
}
if(id==-1)
return -1;
Max=id+m-1;
ans++;
}
return ans;
}
int minimumInstructions(int N, int M, int K,vector<int> C,vector<int> A,vector<vector<int>> B){
n=N,m=M,k=K;
for(int i=0;i<n;i++)
a[i]=C[i];
for(int i=0;i<m;i++){
len[i]=A[i];
for(auto v:B[i])
s[v].push_back(i);
}
for(int i=n-1;i>=0;i--){
for(auto j:s[a[i]]){
if(low[(i&1ll)^1ll][(j+1)%m]!=i+1)
dp[i&1ll][j]=1;
else
dp[i&1ll][j]=dp[(i&1ll)^1ll][(j+1)%m]+1;
low[i&1ll][j]=i;
if(dp[i&1ll][j]>=m)
f[i]=1;
}
}
cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB"<<'\n';
return get();
}