Educational Codeforces Round 165 (Rated for Div. 2)(C dp D贪心 E二维扫点)

C:

由于确定了使用那个最小值,那么他只会单调往左推或者往右推,所以dp

前i个点使用j次的最小值即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,M=2*N,mod=1e9+7;
#define int long long
#define uLL unsigned long long
const long long inf=1e18;
typedef pair<int,int> PII;
typedef long long LL;
using Node=tuple<int,int,int,int>;
using node=tuple<int,int,int,int>;
int n,m,k;
int a[N];
void solve()
{
    cin>>n>>m;
    vector<vector<int>> f(n+10,vector<int>(m+10,inf));
    for(int i=1;i<=n;i++) cin>>a[i];
    f[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++)
        {
            f[i][j]=f[i-1][j]+a[i];
            for(int k=i-1;k>=1;k--)
            {
                if(i-k<=j)
                {
        f[i][j]=min(f[i][j],f[k][j-(i-k)]+(i-k)*a[k]);
        f[i][j]=min(f[i][j],f[k-1][j-(i-k)]+(i-k+1)*a[i]);
                }else break;
            }    
        }
    }
  
    int res=inf;
    for(int i=0;i<=m;i++)
    res=min(res,f[n][i]);
    cout<<res<<"\n";
}
 
signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    cin>>t;
    while(t--) solve();
    return 0;
}

D:如果物品确定,那么Bob为了最小化b数组的总和,肯定最最大的k个b

而alice则会最小化这k个b的a

所以枚举分界点即可

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,M=2*N,mod=1e9+7;
#define int long long
#define uLL unsigned long long
const long long inf=1e18;
typedef pair<int,int> PII;
typedef long long LL;
using Node=tuple<int,int,int,int>;
using node=tuple<int,int,int,int>;
int n,m,k;
PII a[N];
int b[N],c[N];
void solve()
{
    vector<PII> a;
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>b[i];
    for(int i=1;i<=n;i++) cin>>c[i];
    for(int i=1;i<=n;i++){
        if(c[i]-b[i]>=0)
        a.emplace_back(b[i],c[i]);
    }
    sort(a.begin(),a.end(),[&](const auto&p,const auto&q){
        return p.second>q.second;
    });
    if(a.size()<=k){
        cout<<0<<"\n";return ;
    }
    multiset<int> st;
    //priority_queue<PII> q;
    int now=0,res=0;
    int s1=0,s2=0;
    for(int i=0;i<k;i++)
    {
        st.insert(a[i].first);
        s1+=a[i].first;
        //s2+=a[i].second-a[i].first;
    }
    for(int i=k;i<a.size();i++){
        s2+=a[i].second-a[i].first;
    }
    //for(auto [x,y]:a) cout<<x<<" "<<y<<"\n";
    //cout<<s1<<" "<<s2<<"\n";
    for(int i=k;i<a.size();i++)
    {
        res=max(res,s2-s1);
        s2-=(a[i].second-a[i].first);
        st.insert(a[i].first);
        s1+=a[i].first;
        s1-=*st.rbegin();
        st.extract(*st.rbegin());
    }
    cout<<res<<"\n";
}

signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    cin>>t;
    while(t--) solve();
    return 0;
}

E:

问题转成二维扫点

有个结论:如果某个点到右端点不符合要求,那么只需要修改这个点就能保证前面的点都一定符合

要求,即每个右端点存在一个最近的不符合的点,只需要修改一次即可

且肯定要修改最大的左端点

所以用扫描线求出来最右的左端点即可

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,M=2*N,mod=1e9+7;
#define int long long
#define uLL unsigned long long
const long long inf=1e18;
typedef pair<int,int> PII;
typedef long long LL;
using node=tuple<int,int,int>;
int n,m,k;
int a[N];
class segment{
    #define lson (u << 1)
    #define rson (u << 1 | 1)
    public:
    int n;
    vector<int> mn,add,pos;
    segment(int _n) : n(_n) {
        mn.resize(n*4+10,inf);
        pos.resize(n*4+10);
        add.resize(n*4+10);
        build(1,1,n);
    }
    void pushup(int u){
        mn[u]=min(mn[lson],mn[rson]);
        if(mn[u]==mn[rson]) pos[u]=pos[rson];
        else pos[u]=pos[lson];
    }
    void build(int u, int l, int r){
        if (l == r)
        {
            add[u]=0;
            mn[u]=0;
            pos[u]=l;
            return ;
        }
        int mid = (l + r) >> 1;
        build(lson, l, mid);
        build(rson, mid + 1, r);
        pushup(u);
    }
    void pushdown(int u)
    {
        if(add[u]){
            add[lson]+=add[u];
            add[rson]+=add[u];
            mn[lson]+=add[u];
            mn[rson]+=add[u];
        }
        add[u]=0;
    }
    void modify(int u, int l, int r, int L,int R, int val){
        if (l>=L&&r<=R)
        {
            add[u]+=val;
            mn[u]+=val;
            return;
        }
        pushdown(u);
        int mid = (l + r) >> 1;
        if (L <= mid) modify(lson, l, mid, L,R, val);
        if(R>mid) modify(rson, mid + 1, r,L ,R, val);
        pushup(u);
    }
    //二分线段树
    PII query(int u,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R) return {mn[u],pos[u]};
        pushdown(u);
        int mid=l+r>>1;
        PII res={inf,0};
        PII ans={inf,0};
        if(L<=mid) res=query(lson,l,mid,L,R);
        if(R>mid) ans=query(rson,mid+1,r,L,R);
        res.first=min(res.first,ans.first);
        if(res.first==ans.first) res.second=ans.second;
        return res;
    }
};
void solve()
{
    cin>>n;
    vector<int> pre(n+1),nxt(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    {
        vector<int> last(n+1);
        for(int i=1;i<=n;i++){
            pre[i]=last[a[i]];
            last[a[i]]=i;
        }
    }
    {
        vector<int> last(n+1,n+1);
        for(int i=n;i>=1;i--){
            nxt[i]=last[a[i]];
            last[a[i]]=i;
        }
    }
    vector<vector<PII>> pos(n+1);
    for(int i=1;i<=n;i++)
    {
        pos[nxt[i]-1].push_back({i,1});
        pos[i-1].push_back({i,0});
    }
    segment tr(n);
    set<PII> st;
    for(int i=n;i>=1;i--)
    {
        for(auto [x,type]:pos[i]){
            if(type) tr.modify(1,1,n,pre[x]+1,x,1);
            else tr.modify(1,1,n,pre[x]+1,x,-1);
        }
        auto [mn,mnpos]=tr.query(1,1,n,1,i);
        if(mn==0){
           // cout<<mnpos<<"\n";
            st.insert({mnpos,i});
        }
    }
    int ans=0;
    int cur=n+1;
    while(st.size()){
        if(prev(st.end())->second>=cur) 
        st.erase(prev(st.end()));
        else{
            ans++;
            cur=prev(st.end())->first;
            st.erase(prev(st.end()));
        }
    }
    cout<<ans<<"\n";
}

signed main(){
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    cin>>t;

    while(t--) solve();
    return 0;
}
相关推荐
xiaoshiguang319 分钟前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡21 分钟前
【C语言】判断回文
c语言·学习·算法
别NULL23 分钟前
机试题——疯长的草
数据结构·c++·算法
TT哇27 分钟前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
yuanbenshidiaos2 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习2 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA2 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo2 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc2 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
游是水里的游4 小时前
【算法day20】回溯:子集与全排列问题
算法