CF1487E

发布时间 2023-04-24 19:39:03作者: untitled0

\(a,b,c,d\) 分别用 \(a_1,a_2,a_3,a_4\) 表示。

令与第 \(i\) 类食物中的第 \(j\) 个冲突的第 \(i-1\) 类食物的集合为 \(S_{i,j}\)

首先是是人都能看出来的 DP:

\(f_{i,j}\) 为选择第 \(i\) 类食物的第 \(j\) 个时所需的最小费用,显然有

\[f_{i,j}=\min_{k=1}^{k\le n_{i-1},k\notin S_{i,j}}f_{i-1,k}+a_{i,j} \]

现在问题在于这个最小值怎么求。

容易发现,\(S_{i,j}\) 中的元素将序列 \(a_{i-1}\) 划分成了若干区间:

只要求出这些区间的最小值的最小值(不是语病 QAQ)就好了。

静态区间最小值?那自然是 ST 表

使用 vector 存储上述的 \(S_{i,j}\),内部排序后,挨个遍历提取区间即可。

\(\min_{i=1}^{n_4}f_{4,i}\) 即为答案,如果它等于 INF 则证明无解。

时间复杂度显然 \(O(n\log n)\)

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define INF 0x3fffffff
using namespace std;
typedef long long ll;
constexpr int N=1.5e5+5;
int n[5],a[5][N],lg[N],minn[N][20];
//这里直接用a代替f了
void make(int *a,int n){//以a为原数组,建立ST表的函数
    for(int i=1;i<=n;i++)minn[i][0]=a[i];
    for(int i=1;i<=lg[n];i++)
        for(int j=1;j+(1<<i)-1<=n;j++)
            minn[j][i]=min(minn[j][i-1],minn[j+(1<<(i-1))][i-1]);
}
inline int query(int l,int r){
    if(r<l)return INF;
    int len=lg[r-l+1];
    return min(minn[l][len],minn[r-(1<<len)+1][len]);
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    lg[0]=-1;
    for(int i=1;i<=4;i++)cin>>n[i];
    for(int i=1;i<=4;i++)
        for(int j=1;j<=n[i];j++)
            cin>>a[i][j],lg[j]=lg[j/2]+1;
    for(int i=1;i<=3;i++){
        vector<int>unfit[N];
        for(int j=1;j<=n[i+1];j++)unfit[j].push_back(0);//第0个和第n+1个肯定不能选
        int m;cin>>m;
        for(int j=1;j<=m;j++){
            int x,y;cin>>x>>y;
            unfit[y].push_back(x);
        }
        for(int j=1;j<=n[i+1];j++){
            unfit[j].push_back(n[i]+1);
            sort(unfit[j].begin()+1,unfit[j].end()-1);
            //第一个和最后一个一定在正确位置,这样可以稍微减小一点常数(并没什么用
        }
        make(a[i],n[i]);//建立ST表
        for(int j=1;j<=n[i+1];j++){
            int minn=INF;
            for(auto k=unfit[j].begin()+1;k!=unfit[j].end();k++)
                minn=min(minn,query(*(k-1)+1,*k-1));//取区间最小值
            a[i+1][j]=minn==INF?INF:a[i+1][j]+minn;//注意这里不能直接加,INF加任何数都得INF
        }
    }
    int minn=INF;
    for(int i=1;i<=n[4];i++)minn=min(minn,a[4][i]);
    cout<<(minn==INF?-1:minn)<<endl;
    return 0;
}

时限开了 4s,但是 C++ 就跑过了