首页 > 其他分享 >2024牛客暑期多校训练营2 HI

2024牛客暑期多校训练营2 HI

时间:2024-07-20 17:10:35浏览次数:8  
标签:数字 int 区间 多校 贡献 2024 牛客 序列 dp

2024牛客暑期多校训练营2

H.Instructions Substring

题意:

有一个字符串序列,有WSAD四种操作,可以上下左右移动。可以选取一段连续的子序列,从(0,0)出发,经过连续子序列操作后可以经过点(x,y),问这样的子序列有多少个

思路:

若一个子序列能够实现到达点(x,y),那么在这个子序列后面加任意字符都符合要求,因此只需要找到一个最短的合法子序列即可。一个完整的字符串序列可以形成一个路径,若截去前面一段子序列,形成的新路径只需要将原有路径加个前缀和的偏移量即可。我们可以记录下整个序列路径中经过每个点时的下标;然后枚举子序列的起始下标,将(x,y)加上移动路径前缀和的偏移量,看原路径是否经过这个点,经过这个点时的下标是否大于此时的起始下标,若满足则可以二分得到最短合法子序列,然后加上这个贡献n-右边界+1

代码:

#include <bits/stdc++.h>
using namespace std ;
const int MAXN=1e5+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
#define endl "\n"

void solve(){
    int n,x,y;
    cin>>n>>x>>y;
    string s;
    cin>>s;
    if(x==0&&y==0){
        ll ans=n*(n+1)/2;
        cout<<ans<<endl;
        return ;
    }
    map<pair<int ,int >,vector<int > > mp;
    int prex=0,prey=0;
    for(int i=0;i<n;i++){
        if(s[i]=='W'){
            prey++;
        }else if(s[i]=='S'){
            prey--;
        }else if(s[i]=='A'){
            prex--;
        }else{
            prex++;
        }
        mp[{prex,prey}].push_back(i);
    }
    prex=0,prey=0;
    ll ans=0;
    for(int i=0;i<n;i++){
        if(!mp[{x+prex,y+prey}].empty()){
            int left=0,right=mp[{x+prex,y+prey}].size();
            while(left<right){
                int mid=(left+right)/2;
                if(mp[{x+prex,y+prey}][mid]<i){
                    left=mid+1;
                }else{
                    right=mid;
                }
            }
            if(left<mp[{x+prex,y+prey}].size()&&mp[{x+prex,y+prey}][left]<n){
                ans+=n-mp[{x+prex,y+prey}][left];
            }
        }
        if(s[i]=='W'){
            prey++;
        }else if(s[i]=='S'){
            prey--;
        }else if(s[i]=='A'){
            prex--;
        }else{
            prex++;
        }
    }
    cout<<ans<<endl;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t=1;
    
    // cin>>t;
    while(t--){
        solve();
    }
    
    return 0;
}

I.Red Playing Cards

题意:

有2n张卡牌,1到n的所有数字都恰好出现2次。
每次操作,可以选择相同数字的两张牌,删去这两张牌之间的区间(包含这两张牌),其贡献为这个数字大小
区间的牌数,问怎样操作可以使贡献最大

思路:

对于两个区间,若两个区间有重叠部分,则两个区间只能二选一;若两个区间是包含关系,则可以选择先取里面的小区间,再取外面的大区间;若两个区间没有任何重叠,则可以都选取。
区间的贡献为“边界数字大小*区间的牌数”,那么可以看作这个区间每个数的贡献为“边界的数字大小”.对于两个区间是包含关系的情况,可以知道若“小区间的数字大小”大于“大区间的数字大小”,那么先取小区间再取大区间可以最大化贡献,反之没有意义。
因此我们可以先预处理出每个数字的左右边界,从大到小遍历这些数字,得出每个数字的区间的最大贡献,最后考虑没有重叠的区间的贡献相加(增加0的区间来实现)。
\(f_i\)为数字i的区间的最大贡献,\(dp_j\)为数字i区间内部的每张牌的贡献的前缀和。
状态转移方程为:
\(dp_j\)=\(dp_{j-1}\)+i
\(dp_{r[a[j]]}\)=\(dp_{j-1}\)+\(f_{a[j]}\) (如果\(j=l[a[j]]\)并且\(r[a[j]]<r[i]\))

代码:

#include <bits/stdc++.h>
using namespace std ;
const int MAXN=1e5+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
#define endl "\n"

void solve(){
    int n;
    cin>>n;
    int a[2*n+1];
    for(int i=1;i<=2*n;i++){
        cin>>a[i];
    }
    vector<int > l(n+1,-1),r(n+1);
    for(int i=1;i<=2*n;i++){
        if(l[a[i]]==-1){
            l[a[i]]=i;
        }else{
            r[a[i]]=i;
        }
    }
    l[0]=0;
    r[0]=2*n+1;
    vector<int > f(n+1,0);
    for(int i=n;i>=0;i--){
        vector<int > dp(2*n+1,0);
        for(int j=l[i]+1;j<r[i];j++){
            dp[j]=max(dp[j],dp[j-1]+i);
            if(j==l[a[j]]&&r[a[j]]<r[i]){
                dp[r[a[j]]]=max(dp[r[a[j]]],dp[j-1]+f[a[j]]);
            }
        }
        f[i]=2*i+dp[r[i]-1];
    }
    cout<<f[0]<<endl;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t=1;
    
    // cin>>t;
    while(t--){
        solve();
    }
    
    return 0;
}

标签:数字,int,区间,多校,贡献,2024,牛客,序列,dp
From: https://www.cnblogs.com/beater/p/18313393

相关文章

  • 2024 暑假友谊赛 2
    B.TilingChallenge1.我的方法是按顺序遍历,遇到'.'时就检查一下它的上下左右是不是都是点,如果都是点的话,标记这个点,把这个点和他上下左右都标记为‘?’,但是要加一个条件,如果‘.’的个数不是5的倍数就不符合题意,不加这个会wa37,我也不知道为什么#include<bits/stdc++.h>#defin......
  • NOI 2024 退役记
    NOI2024结束了,我的OI生涯也告一段落了。怎么说呢,今年变动挺大的,Day1之前确实完全没有做好准备,考场策略全乱套了,最后Day1考151分,直接垫底了,T1是个小难写的ds,我做法反正很难写且常数大,最后调完了也没卡常卡过去,拿了80,还浪费了巨大长时间,场上看到有了pretest我也很......
  • 2024牛客暑期多校训练营2
    H.InstructionsSubstring题目大意:给出一段长为\(n\)的字符串,其中WASD分别代表向上下左右走,给出目的地\((x,y)\),选择一段连续子序列使得从\((0,0)\)出发可以经过目的地,求这样的子序列的总数。思路:用前缀和记录到\(i\)为止到达的位置,从前往后遍历右端点\(r\),找到恰好到......
  • 2024杭电第一场
    1012并考虑两维坐标都离散化后枚举每个格子,用二维前缀和计算其被覆盖的次数,设为cnt则k固定时该格子的贡献为:(1-C[n-cnt][k]/C[n][k])*该格子的面积注意事项:1.处处取模2.给的是点坐标,不是格点坐标,因此计算二维前缀和时要x++,y++#include<bits/stdc++.h>usingnames......
  • NOI2024退役记
    本文仍在不断更新中不知不觉我的OI生涯就结束了,走到这一步才突然发觉。才想起来我还有好多感兴趣的内容没学,还有好多有趣的题没做,还有好多流逝的时间没有抓住。但是都已经走到这一步了,高中的OI之旅就也只能到此为止了。Day-?提前两周来到了重庆,跟着梦熊的模拟赛,又认识了一个......
  • 【2024-ZR-C Day 4】图论(1)
    1.强连通分量1.1.定义在有向图中,选取一个点集\(S\),若对于\(S\)中的任意两点\(u,v\),都满足\(u\)可以到达\(v\),则称\(S\)是强连通的。强连通分量是图中一个极大的强连通的点集。性质:把一个有向图通过强连通分量缩点后,新的图是一个DAG.1.2.Kosaraju算法在无向图......
  • 牛客小白月赛98补题
    D一道很典型的区间DP//区间DP典题#include<bits/stdc++.h>usingnamespacestd;#definelllonglongconstintN=520;lln,L,R;strings;llsum0[N],sum1[N];llf[N][N];voidsolve(){cin>>n>>L>>R>>s;s='......
  • C基础(学习)2024.7.19
             Linux基本命令,vi编译器的使用,简单的编程步骤,程序语言,gcc编译器编译过程,进制转换相关知识可以查看文档http://t.csdnimg.cn/CmqhC        数值表示,词法符号,变量,常量相关知识可以查看文档http://t.csdnimg.cn/jJIe2        运算符和输表达式......
  • 2024/7/20周末总结
    本周,我完美完成了PTA基础编程题目集中的函数部分。对阶乘计算的进阶方法这道上周无法通过的题目进行了学习和复现通过。对超大数的输出方式有了新的理解。同时,完成了编程题三分之一的题目,其中,由于BCD数中需要实现位运算而有些难以理解外,其他均以C++通过。关于本周Java的学习,......
  • 【北航主办丨本届SPIE独立出版丨已确认ISSN号】第三届智能机械与人机交互技术学术会议
    由北京航空航天大学指导,北京航空航天大学自动化科学与电气工程学院主办,AEIC学术交流中心承办的第三届智能机械与人机交互技术学术会议(IHCIT2024)将定于2024年7月27日于中国杭州召开。大会面向基础与前沿、学科与产业,旨在将“人工智能”、“智能系统”和“人机交互”等学......