洛谷笔记 Day 4(2)

T1

link

思路

考虑拆开这个式子,把 \(\max\) 式子和 \(\min\) 式子分别处理,接着考虑贡献,一个数能够成为 \(\max\)\(\min\) 当且仅当左右都没有比它小的或者大的,笛卡尔树或者单调栈处理就行。

代码

#include<bits/stdc++.h>
using namespace std;
int a[300005],l[300005],r[300005];
int main(){
    int n;cin>>n;
    long long ans=0;
    vector<int>st;
    for (int i=1;i<=n;++i)cin>>a[i];
    for (int i=1;i<=n;++i){
        while (!st.empty()&&a[st.back()]<a[i])st.pop_back();
        l[i]=st.empty()?1:st.back()+1;
        st.push_back(i);
    }
    st.clear();
    for (int i=n;i>=1;--i){
        while (!st.empty()&&a[st.back()]<=a[i])st.pop_back();
        r[i]=st.empty()?n:st.back()-1;
        st.push_back(i);
    }
    for (int i=1;i<=n;++i)ans+=1LL*a[i]*(i-l[i]+1)*(r[i]-i+1);
    st.clear();
    for (int i=1;i<=n;++i){
        while (!st.empty()&&a[st.back()]>a[i])st.pop_back();
        l[i]=st.empty()?1:st.back()+1;
        st.push_back(i);
    }
    st.clear();
    for (int i=n;i>=1;--i){
        while (!st.empty()&&a[st.back()]>=a[i])st.pop_back();
        r[i]=st.empty()?n:st.back()-1;
        st.push_back(i);
    }
    for (int i=1;i<=n;++i)ans-=1LL*a[i]*(i-l[i]+1)*(r[i]-i+1);
    
    cout<<ans;
}

T2

link

题意

一个长度为 \(n\) 的价值序列 \(a\),选出一个子序列,不能有连着的 \(k\) 个,问最后能得到的最大价值是多少。

思路

考虑 DP,定义 \(f_{i}\) 表示前 \(i\) 个你能得到的最大价值,两个情况,\(i\) 不选和 \(i\) 要选,先说选的,贪心的来讲 \(a\) 没有负数,你肯定是要尽可能多选的,但是由于题目限制必须要有一个断点,我们枚举这个断点 \(j\),表示不选 \(j\) 你得到的最大价值,则有转移式 \(f_i=\max f_{j-1}+sum_{i}-sum{j+1-1}=\max f_{j-1}+sum{i}-sum_{j}\),依旧提取定值,\(f_i=\max\{f_{j-1}-sum_{j}\}+sum_{i}\),由于 \(j\) 只能在 \([i-1,i-k-1]\),我们单调队列维护这个值即可。

代码

#include<bits/stdc++.h>
using namespace std;
long long a[100005],f[100005],sum[100005];
long long w(int x){
    if(x==0)return 0;
    return f[x-1]-sum[x];
}
int main(){
    int n,k;cin>>n>>k;
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1;i<=n;++i)sum[i]=sum[i-1]+a[i];
    deque<int>st;
    st.push_back(0);
    for(int i=1;i<=n;++i){
        while(!st.empty()&&w(st.back())<=w(i))st.pop_back();
        st.push_back(i);
        while(!st.empty()&&st.front()<i-k)st.pop_front();
        f[i]=max(f[i-1],sum[i]+w(st.front()));
    }
    cout<<f[n]<<'\n';
}
//f[i]=sum[i]-sum[j]+f[j-1]

T3

开车旅行,不想写了

T4

link

思路

先考虑小的问题。如果 \(2\times 2\) 的显然是简单的,直接不放公主那里就可以了,我们考虑扩展到 \(4 \times 4\),我们先按照上面的方法处理一个 \(2\times 2\),然后剩下一个 L,我们把中间放上一个,这样每个 \(2\times 2\) 都会缺一个,直接补上即可。分治解决。

代码

毒瘤代码。

#include<bits/stdc++.h>
using namespace std;
int k,x,y,n;
void dfs(int l,int r,int u,int d,int x0,int y0){
    if(l==r||u==d)return;
    int mx=(l+r)/2,my=(u+d)/2;
    if(x0<=mx&&y0<=my){
        cout<<mx+1<<' '<<my+1<<" 1\n";
        dfs(l,mx,u,my,x0,y0);
        dfs(mx+1,r,u,my,mx+1,my);
        dfs(l,mx,my+1,d,mx,my+1);
        dfs(mx+1,r,my+1,d,mx+1,my+1);
    }
    else if(x0<=mx&&y0>my){
        cout<<mx+1<<' '<<my<<" 2\n";
        dfs(l,mx,u,my,mx,my);
        dfs(mx+1,r,u,my,mx+1,my);
        dfs(l,mx,my+1,d,x0,y0);
        dfs(mx+1,r,my+1,d,mx+1,my+1);
    }
    else if(x0>mx&&y0<=my){
        cout<<mx<<' '<<my+1<<" 3\n";
        dfs(l,mx,u,my,mx,my);
        dfs(mx+1,r,u,my,x0,y0);
        dfs(l,mx,my+1,d,mx,my+1);
        dfs(mx+1,r,my+1,d,mx+1,my+1);
    }
    else{
        cout<<mx<<' '<<my<<" 4\n";
        dfs(l,mx,u,my,mx,my);
        dfs(mx+1,r,u,my,mx+1,my);
        dfs(l,mx,my+1,d,mx,my+1);
        dfs(mx+1,r,my+1,d,x0,y0);
    }
}
int main(){
    cin>>k>>x>>y;
    n=1<<k;
    dfs(1,n,1,n,x,y);
}

T5

奶牛浴场

思路

论文题。我们考虑最优的情况肯定出在边上有点的方案中,那么我们考虑从左往右扫一遍,嵌定第 \(i\) 个点为左端点,然后再去枚举右端点,需要注意的是,我们在遇到点时要动态更新上下界。有一个细节是要把四个角的点也要加进去并且要判空。

代码

#include<bits/stdc++.h>
using namespace std;
struct node{
    int x,y;
}a[5005];
bool cmp1(node xx,node yy){
    return xx.x<yy.x;
}
bool cmp2(node xx,node yy){
    return xx.y<yy.y;
}
int main(){
    int L,W;cin>>L>>W;
    int n;cin>>n;
    a[1].x=0;a[1].y=0;
    a[2].x=0;a[2].y=W;
    a[3].x=L;a[3].y=0;
    a[4].x=L;a[4].y=W;
    for (int i=5;i<=n+4;++i){
        cin>>a[i].x>>a[i].y;
    }
    n+=4;
    sort(a+1,a+n+1,cmp1);
    int ans=0;
    for (int i=1;i<=n;++i){
        int l=0,r=W;
        for (int j=i+1;j<=n;++j){
            if(a[j].x==a[i].x)continue;
            ans=max(ans,(a[j].x-a[i].x)*(r-l));
            if(a[j].y<a[i].y)l=max(l,a[j].y);
            else if(a[j].y>a[i].y)r=min(r,a[j].y);
            else break;
        }
    }
    for (int i=n;i>=1;--i){
        int l=0,r=W;
        for (int j=i-1;j>=1;--j){
            if(a[j].x==a[i].x)continue;
            ans=max(ans,(a[i].x-a[j].x)*(r-l));
            if(a[j].y<a[i].y)l=max(l,a[j].y);
            else if(a[j].y>a[i].y)r=min(r,a[j].y);
            else break;
        }
    }
    sort(a+1,a+n+1,cmp2);
    for(int i=1;i<=n-1;++i){
        ans=max(ans,L*(a[i+1].y-a[i].y));
    }
    cout<<ans;
}

T6

link

题意

给定一棵依次插入元素的 BST,最小化 BST 插入元素序列的字典序。

思路

考虑你的 BST 是以时间戳来作为小根堆性质的 treap,这题的精髓就在于,treap 和笛卡尔树是以值域为小根堆性质的树,那么我们交换时间戳和值,建笛卡尔树即可。

T7

机器人

思路

卡了 \(n\) 年的题目。。。

我们看到数据范围能猜出是倍增,但是不知带怎样划分这个倍增的轮数,考虑我们把如下刻画为一轮:从 \(i\) 起跳,回到 \(i\) 再跳,直到离开 \(i\) 这个跳台。定义 \(f_{i,j}\) 表示从 \(i\) 起跳,跳 \(2^j\) 所到的跳台,这个东西是可以倍增处理的,同时再维护这些所需要的时间 \(g_{i,j}\)

考虑如何处理 \(f_{i,0}\)\(g_{i,0}\),发现一轮你来来回回跳是不会跳出 \(\log n\) 次的,模拟就行了,可以维护当前跳出的距离,然后判断是否到一下个跳台,最后二分。

接着是答案的查询,先找到距离起始点最近的跳台 \(y\),然后判掉一些边界,跳一下,然后把剩下的时间都走了就行,太细节了吓哭了。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+5,INF=4e18;
int f[N][25],g[N][25],n,x[N],p[N];
int query(int s,int t){
    int y=upper_bound(x+1,x+n+1,s)-x-1;
    if(y<1||x[y]<=s-t)return s-t;
    t-=s-x[y];
    for(int j=20;j>=0;--j)if(t>=g[y][j])t-=g[y][j],y=f[y][j];
    if(!t)return x[y];
    int dis=p[y];
    while(dis+1<t)t-=dis+1,dis*=2;
    return x[y]+dis-(t-1);
}
signed main(){
    ios::sync_with_stdio(0),cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i)cin>>x[i]>>p[i];
    x[n+1]=INF,f[n][0]=n,g[n][0]=INF;
    for(int i=1;i<n;++i){
        int dis=p[i];
        g[i][0]=0;
        while(dis<x[i+1]-x[i])g[i][0]+=dis+1,dis<<=1;
        f[i][0]=upper_bound(x+1,x+n+2,x[i]+dis)-x-1;
        g[i][0]+=1+x[i]+dis-x[f[i][0]];
    }
    for(int j=1;j<=20;++j)for(int i=1;i<=n;++i){
        f[i][j]=f[f[i][j-1]][j-1];
        g[i][j]=min(INF,g[i][j-1]+g[f[i][j-1]][j-1]);
    }
    int q;cin>>q;
    while(q--){
        int s,t;cin>>s>>t;
        cout<<query(s,t)<<'\n';
    }
    return 0;
}

T8

link

思路

枚举每个活动,考虑后面是否有比其更有价值的活动。

  • 有,我们就刚好留下 \(E\) 的能量(算上后面新加的),剩下的在这个活动用掉。
  • 没有,全在这里用了就行。

可以单调栈处理。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int v[100005],l[100005];
signed main(){
    int T;cin>>T;
    for(int cas=1;cas<=T;++cas){
        int e,r,n;cin>>e>>r>>n;
        int ee=e;
        long long tot=0;
        for(int i=1;i<=n;++i)cin>>v[i];
        vector<int>st;
        for(int i=n;i>=1;--i){
            while(!st.empty()&&v[st.back()]<=v[i])st.pop_back();
            if(!st.empty())l[i]=st.back();
            else l[i]=0;
            st.push_back(i);
        }
        for(int i=1;i<=n;++i){
            int ne;
            if(!l[i]){
                tot+=1ll*v[i]*e;
                e=0;
            }
            else{
                int j=l[i];
                if(e+1ll*r*(j-i)>ee){
                    int use=e+1ll*r*(j-i)-ee;
                    if(use>e)use=e;
                    tot+=1ll*v[i]*use;
                    e-=use;
                }
            }
            e=min(e+r,ee);
        }
        cout<<"Case #"<<cas<<": "<<tot<<'\n';
    }
}

T9

link

不会咕着吧。

posted @ 2026-02-16 17:42  Cefgskol  阅读(0)  评论(0)    收藏  举报