2024SMU蓝桥3训练补题

ps:这个时候蓝桥已经结束一个星期了。。我才把这个训练的题补完。

D-P8715 [蓝桥杯 2020 省 AB2] 子串分值 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:对于每一个位置的字母,预处理出这个字母左边第一个出现的下标,和右边第一个出现的下标。

对应这个位置字母的贡献为,左边可选个数+右边可选个数+左边可选个数*右边可选个数。 左边可选和右边可选的个数都可以通过查询预处理的下标数组,计算得出。

void solve(){           //D         //思维
    string str; cin>>str;
    int n=str.size();
    int arrL[n],arrR[n];
    int idxL[26],idxR[26];
    for(int i=0;i<26;i++) idxL[i]=-1,idxR[i]=-1;  //init
    for(int i=0;i<n;i++){
        int c=str[i]-'a';
        arrL[i]=idxL[c];
        idxL[c]=i;
    }
    for(int i=n-1;i>=0;i--){
        int c=str[i]-'a';
        arrR[i]=idxR[c];
        idxR[c]=i;
    }
    int ans=n;
    for(int i=0;i<n;i++){
        int numL,numR;
        if(arrL[i]==-1) numL=i;
        else numL=i-arrL[i]-1;
        if(arrR[i]==-1) numR=n-i-1;
        else numR=arrR[i]-i-1;
        ans+=numL+numR+numL*numR;
    }
    cout<<ans;
}

ps:这题赛时是写出来了,但是还是值得贴在这里。

E-P8712 [蓝桥杯 2020 省 B1] 整数拼接 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:预处理。切记!!此题要处处取模,不然会爆。

int cnt1[12][100005];
void solve(){                                                  //E   经典 数字拼接题
    int n,k; cin>>n>>k;
//    int f1[12]={0},f2[12]={0};   //f1含义为数字长度为i,且是k倍数的数字的个数;f2含义为,数字乘以10^i之后是k的倍数的个数--因为数字最大到1e9,开12即可
    int arr[100005];
//    map<pair<int,int>,int> mp1,mp2;    //m1p:<x,y>,z----乘10^x后余k为y的数有z个;;;;mp2;<x,y>,z----数字长度为x,余k后为y的数字有z个
    for(int i=1;i<=n;i++){
        cin>>arr[i];
        int x=arr[i]%k;
        for(int j=1;j<=10;j++) cnt1[j][(x*(int)pow(10,j))%k]++;
//        mp2[{(int)log10(x)+1,x%k}]++;
//        for(int j=1;j<10;j++) mp1[{j,(x*(int)pow(10,j))%k}]++;
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        int num=(int)log10(arr[i])+1;
        int x=arr[i]%k;
        if(x==0) ans+=cnt1[num][0];
        else ans+=cnt1[num][k-x];
    }
    for(int i=1;i<=n;i++){                  //减去自己拼自己的
        int num=log10(arr[i])+1;
        if( (arr[i]%k*(int)pow(10,num)+arr[i])%k==0 ) ans--;

    }
    cout<<ans;
}

F-P8725 [蓝桥杯 2020 省 AB3] 画中漂流 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:线性dp!

int dp[3005][2005]={0};       //时间到i秒,剩下j体力
void solve(){           //F
    int d,t,m; cin>>d>>t>>m;   //往下游走>=d就死---坚持t秒---可向上m次
    dp[0][m]=1;
    for(int i=1;i<=t;i++){
        for(int j=0;j<=m;j++){
            if( (m-j)-(i-(m-j))+d<=0 ) continue;   //m-j为前进,i-(m-j)为后退
            dp[i][j]=(dp[i-1][j]+dp[i-1][j+1])%1000000007;
        }
    }
    cout<<dp[t][0];
}

H-P8703 [蓝桥杯 2019 国 B] 最优包含 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:dp。

int dp[1003][1003];    //考虑前i个s的字符,包含t中前j个字符所需的代价
void solve(){                   //H
    string s,t; cin>>s>>t;
    memset(dp,0x3f3f3f3f,sizeof(dp));
    for(int i=0;i<s.size();i++) dp[i][0]=0;
    for(int i=0;i<s.size();i++){
        for(int j=0;j<=i;j++){                    //dp数组整体位移一位
            if(s[i]!=t[j]) dp[i+1][j+1]=min(dp[i][j]+1,dp[i][j+1]);   //修改 或 不修改
            else dp[i+1][j+1]=dp[i][j];
        }
    }
    cout<<dp[s.size()][t.size()];
}

I-P8710 [蓝桥杯 2020 省 AB1] 网络分析 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:并查集+懒标记。在两个节点合并(即两个集合合并之前),把懒标记加到各自集合中,再进行合并。懒标记加完要清零,且最后还要下放剩余的懒标记到其集合中。

ps:196ms;102.69MB 
int n,m;
int fa[10004],lag[10004],ans[10004];
vector<int> sett[10004];
int find(int x){
    if(x!=fa[x]) fa[x]=find(fa[x]);
    return fa[x];
}
void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) fa[i]=i,sett[i].emplace_back(i);
    for(int i=1;i<=m;i++){
        int op,x,y; cin>>op>>x>>y;
        if(op==1){                          //每次连接节点之前,下放标记。
            if(x>y) swap(x,y);
            int fa1=find(x),fa2=find(y);
            if(fa1!=fa2){
                if(lag[fa1]!=0) for(auto v:sett[fa1]) ans[v]+=lag[fa1];    //把懒标记下放到其集合中
                lag[fa1]=0;
                if(lag[fa2]!=0) for(auto v:sett[fa2]) ans[v]+=lag[fa2];
                lag[fa2]=0;
                for(auto v:sett[fa2]) sett[fa1].emplace_back(v);
                sett[fa1].emplace_back(y);       //fa1这个集合中加入点y--wa!!!这样只是添加了一个点,y集合的其他点会被丢弃!!
                fa[fa2]=fa1;
            }
        }
        else if(op==2) lag[find(x)]+=y;   //把标记加到父结点
    }
    for(int i=1;i<=n;i++){
        if(lag[i]!=0){
            for(auto v:sett[i]) ans[v]+=lag[i];
        }
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
}
ps:929ms;1.37MB 
int n,m;
int fa[10004],lag[10004],ans[10004];
vector<int> sett[10004];
int find(int x){
    if(x!=fa[x]) fa[x]=find(fa[x]);
    return fa[x];
}
void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) fa[i]=i,sett[i].emplace_back(i);
    for(int i=1;i<=m;i++){
        int op,x,y; cin>>op>>x>>y;
        if(op==1){
            if(x>y) swap(x,y);
            int fa1=find(x),fa2=find(y);
            if(fa1!=fa2){
                for(int i=1;i<=n;i++) ans[i]+=lag[find(i)];
                for(int i=1;i<=n;i++) lag[i]=0;
                sett[fa1].emplace_back(y);
                fa[fa2]=fa1;
            }
        }
        else if(op==2) lag[find(x)]+=y;
    }
    for(int i=1;i<=n;i++) cout<<ans[i]+lag[find(i)]<<" ";
}

ps:自己写的在时间上更优于普通的写法。因为避免了很多不必要的lag的遍历。普通写法是只要集合合并,就下放全部懒标记。自己写的是合并哪两个集合,就下放哪两个集合的懒标记。

J-P8724 [蓝桥杯 2020 省 AB3] 限高杆 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:法一--分层建图,建一个三层的图,分别表示恢复0条边,恢复1条边,恢复2条边。共需要3倍空间。

法二:正常建图。动态规划思想--dis[10004][3]--表示到达点 i,恢复j(0,1,2)条边时的最短路。

法一:204ms;17.16MB
int n,m;
const int inf=0x3f3f3f3f;
vector<pair<int,int>> vct[30004];
priority_queue<pair<int,int>> pq;
int dis[30004],vis[30004];
void dijkstr(int s){
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    pq.emplace(0,s);
    while(pq.size()){
        int from=pq.top().second;
        pq.pop();
        if(vis[from]) continue;
        vis[from]=1;
        for(auto v:vct[from]){
            int to=v.first,weight=v.second;
            if(dis[to]>dis[from]+weight){
                dis[to]=dis[from]+weight;
                pq.emplace(-dis[to],to);
            }
        }
    }
}
void solve(){               //分层图!!               !法一!
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,w,c; cin>>u>>v>>w>>c;
        //用以下方式建一个立体的三层图,跑正常的dijkstra即可。恢复一条边的最短距离为dis[2*n];恢复两条边的最短距离为dis[3*n];
        //此题恢复的边一定会用上那条边,不一定恢复两条边会比恢复一条边短,或比不恢复边短。取min即可。
        if(c==0){               //没有障碍,三层都在本层正常建边
            vct[u].emplace_back(v,w);  //第一层
            vct[v].emplace_back(u,w);
            vct[u+n].emplace_back(v+n,w);  //第二层
            vct[v+n].emplace_back(u+n,w);
            vct[u+2*n].emplace_back(v+2*n,w);  //第三层
            vct[v+2*n].emplace_back(u+2*n,w);
        }
        else{       //有障碍,第一层连向第二层,第二层连向第三层。
//            vct[u].emplace_back(v+n,w);   //这4行是错的。
//            vct[v+n].emplace_back(u,w);
//            vct[u+n].emplace_back(v+2*n,w);
//            vct[v+2*n].emplace_back(u+n,w);
            vct[u].emplace_back(v+n,w);   //单向的!!一层向下一层!!,否则可能会从下层更新上层的点
            vct[v].emplace_back(u+n,w);
            vct[u+n].emplace_back(v+2*n,w);
            vct[v+n].emplace_back(u+2*n,w);
        }
    }
    dijkstr(1);
    cout<<dis[n]-min({dis[n],dis[2*n],dis[3*n]});   //不一定恢复的边越多越短!直接取三个的最小值准没错!!
}
法二:148ms;10.16MB
int n,m;
typedef struct myp1{
    int v,w,typ;
}myp1;
typedef struct myp2{
    int d,node,take;
    friend bool operator <(const myp2 a,const myp2 b){    //只能重载小于号!!
        return a.d>b.d;      //按d的从小到大排序
    }
}mpy2;
const int inf=0x3f3f3f3f;
vector<myp1> vct[10004];
priority_queue<myp2> pq;
//priority_queue<pair<int,pair<int,int>>> pq;
int dis[10004][3],vis[10004][3];
void dijkstra(int s){
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s][0]=0;
    pq.emplace((myp2){0,s,0});
    while(pq.size()){
        int from=pq.top().node;
        int take=pq.top().take;
        pq.pop();
        if(vis[from][take]) continue;
        vis[from][take]=1;
        for(auto v:vct[from]){
            int to=v.v,weight=v.w,typ=v.typ;
            if(take+typ>2) continue;
            if(dis[to][take+typ]>dis[from][take]+weight){
                dis[to][take+typ]=dis[from][take]+weight;
                pq.emplace(myp2{dis[to][take+typ],to,take+typ});
            }
        }
    }
}
void solve(){         //不建分层图,用动态规划思想.
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,w,t; cin>>u>>v>>w>>t;
        vct[u].emplace_back((myp1){v,w,t});
        vct[v].emplace_back((myp1){u,w,t});
    }
    dijkstra(1);
    cout<<dis[n][0]-min({dis[n][0],dis[n][1],dis[n][2]});     //不一定恢复的边越多越短!直接取三个的最小值准没错!!
}

ps:似乎动态规划思路在时间空间上都略优于分层图。 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值