2019牛客暑期多校训练营(第四场)补题总结(未完待续)

K.number

题意:给一个只有数字构成的字符串,问能整除300的子串有多少

思路:我队友给我描述他写这道题的想法,很神奇,他当时看到这道题脑子里突然浮现了三个光束轮流转的画面,然后一下就写出来了。。。实际上这道题就是三个状态的转移。若我们能找到两个连续的0,那我们就只要找到前面的子串中与这两个0相连的子串中有多少是3的倍数就行了,假如我们确定了与第i字符相连的子串大小模3是0,1,2的个数,则第i+1个(此处省略)可以通过第i+1个字符大小转移状态,最终得到答案

代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn=1e5+9;
int cnt[3],tmp[3];
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int i,j,k;
    string s;
    cin>>s;
    int len=s.size();
    ll ans=0;
    for(i=0;i<len;i++){
        if(s[i]=='0'){
            ans++;
            if(i+1<len&&s[i+1]=='0')ans+=cnt[0]+1;
        }
        int x=s[i]-'0';
        for(j=0;j<3;j++){
            tmp[(j+x)%3]=cnt[j];
        }
        tmp[x%3]++;
        for(j=0;j<3;j++){
            cnt[j]=tmp[j];
        }
    }
    cout<<ans<<endl;
}

 

J.free

题意:给你n个点,m条边,每条边上有一个值代表通过这条边的花费,给你一个起点和一个终点,并且允许你免费通过k条边,问最短路径是多少

思路:分层最短路板子题,相当于建了一个立体的图,层层覆盖。开辟一个储存路径长度的二维数组d[i][j],代表起点到i点免费通过j条边的最短路径花费,建立一个状态转移式d[i][j]=min(d[v][j]+w[i][v],d[i][j-1])就行了

代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define pi pair<int,int>
const int maxn=1e3+9;
int d[maxn][maxn],cnt,k,head[maxn];
struct Edge{
    int val,to,next;
}edge[maxn*2];
void init(){
    memset(head,-1,sizeof(head));
    cnt=0;
}
struct node{
    int dist,pt,pile;
    node(int dist,int pt,int pile):dist(dist),pt(pt),pile(pile){}
    bool operator < (const node &a)const{
        if(dist==a.dist)return pile >a.pile;
        return dist>a.dist;
    }
};
void add(int u,int v,int val){
    edge[cnt].next=head[u];
    edge[cnt].val=val;
    edge[cnt].to=v;
    head[u]=cnt++;
}
int djk(int s){
    memset(d,inf,sizeof(d));
    d[s][0]=0;
    priority_queue<node>q;
    q.push(node(0,s,0));
    while(!q.empty()){
        node nd=q.top();;
        int u=nd.pt;
        q.pop();
        int j=nd.pile;
  //      if(vis[u][j])continue;
  //      vis[u][j]=1;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            int w=edge[i].val;
    //        if(vis[v][j])goto f;
            if(d[v][j]>d[u][j]+w){
                d[v][j]=d[u][j]+w;
                q.push({d[v][j],v,j});
            }
        //    f:if(vis[v][j+1])continue;
            if(j+1<=k&&d[v][j+1]>d[u][j]){
                d[v][j+1]=d[u][j];
                q.push({d[v][j+1],v,j+1});
            }
        }
    }
 
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    init();
    int i,j,n,m,s,t;
    cin>>n>>m>>s>>t>>k;
    while(m--){
        int x,y,z;
        cin>>x>>y>>z;
        add(x,y,z);add(y,x,z);
    }
    djk(s);
    cout<<d[t][k]<<endl;
}

 

A:meeting

题意:给你一颗树,树上的一些点上有人,他们同时出发,将在一点汇合,请你选出汇合的点使得汇合时间最短

思路:树的直径,懂的都懂

代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=100005;
struct Edge{
    int to,next;
}edge[maxn*4];
int cnt=0,per[maxn],d[maxn],head[maxn];
void add(int u,int v){
    edge[cnt].next=head[u];
    edge[cnt].to=v;
    head[u]=cnt++;
}
void dfs(int u,int pre,int &pt,int &mx){
    int i,j,k;
    for(i=head[u];i!=-1;i=edge[i].next){
       // cout<<123<<endl;
        int to=edge[i].to;
        if(to==pre)continue;
        d[to]=d[u]+1;
        if(per[to]){
            if(d[to]>=mx){
                mx=d[to];
                pt=to;
            }
        }
        dfs(to,u,pt,mx);
    }
    return;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int i,j,k,n;
    cin>>n>>k;
     
    memset(head,-1,sizeof(head));
    for(i=0;i<n-1;i++){
        int x,y;cin>>x>>y;
        add(x,y);
        add(y,x);
    }
    int st;
    for(i=0;i<k;i++){
        int x;cin>>x;
        per[x]=1;
        st=x;
    }
    int pt=1,mx=-1;
    dfs(st,st,pt,mx);
    memset(d,0,sizeof(d));
    dfs(pt,pt,pt,mx);
    cout<<(d[pt]+1)/2<<endl;
}

D.triples I

题意:给你一个数n,要求你找到最少的是3的倍数的数按位或得到这个数并且输出这些数

思路;我们确定答案只有可能是1个数或者2个数,假若n%3==0的话就直接输出n本身就行了,否则答案必定是两个数,我们将n转换成二进制,然后任意取出三个位置的1,并且将这三个位置的1分别按所在位数转换成对应的十进制(比如10101,转换成10进制分别是16,4,1),我们发现取出来的数模3的余数只有可能是1和2,我们发现无论他们的余数是哪一种都存在一种组合方式让他们的加和模3余0,他们的加和算作是一个答案,然后我们令x=n%3,然后用n减去之前的组合中余数为x的数就是另一个答案,证明就不写了,自己随便想想就出来了

代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const ll maxn=1e5+9;
ll lowbit(ll x){
    return x&-x;
}
int main(){
    int i,j,k,n,t;
    cin>>t;
    while(t--){
        ll x;cin>>x;
        ll a1,a2,a3;
        a1=lowbit(x);
        a2=lowbit(x-a1);
        a3=lowbit(x-a1-a2);
        if(x%3==0){
            cout<<1<<' '<<x<<endl;
        }
        else{
            cout<<2<<' ';
            if((a1+a2)%3==0){
                if((x%3)==(a1%3)){
                    cout<<x-a1<<' '<<a1+a2<<endl;
                }
                else if(x%3==a2%3){
                    cout<<x-a2<<' '<<a1+a2<<endl;
                }
            }
            else if((a2+a3)%3==0){
                if(x%3==a3%3){
                    cout<<x-a3<<' '<<a3+a2<<endl;
                }
                else if(x%3==a2%3){
                    cout<<x-a2<<' '<<a3+a2<<endl;
                }
            }
            else if((a1+a3)%3==0){
                if(x%3==a1%3){
                    cout<<x-a1<<' '<<a1+a3<<endl;
                }
                else if(x%3==a3%3){
                    cout<<x-a3<<' '<<a1+a3<<endl;
                }
            }
            else{
                if(x%3==(a1+a2)%3){
                    cout<<x-(a1+a2)<<' '<<a1+a2+a3<<endl;
                }
                else if(x%3==a1%3){
                    cout<<x-a1<<' '<<a1+a2+a3<<endl;
                }
            }
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值