2019牛客暑期多校赛(第四场)

A题  https://ac.nowcoder.com/acm/contest/884/A

题意:给定n个点和n-1条边构成一颗树,k个人,最后一行给出k个人的位置来,问所有人到达树的一点会面所用的最短时间是什么,所需最短时间是每个人到达那个地方的最长时间。

题解:类似求树的直径,从所给的人的位置为起点,然后求到另一个给的人的位置的最大值,找到最大值的这个位置再搜一遍最大值,这个距离再除2向上取整就是答案了。

官方题解:

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
const int inf = 0x3f3f3f3f;
int dir[8][2]={{1,0},{0,1},{1,1},{1,-1},{-1,1},{-1,-1},{0,-1},{-1,0}};
#define pi acos(-1)
#define ls rt<<1
#define rs rt<<1|1
#define me0(s) memset(s,0,sizeof(s))
#define me1(s) memset(s,1,sizeof(s))
#define mef(s) memset(s,-1,sizeof(s))
#define meinf(s) memset(s,inf,sizeof(s))
const int N=200005;
inline int read() {
    char c=getchar(); int x=0, f=1;
    while(c<'0'|c>'9') {if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
ll q_pow(ll a,ll b,ll mod){
    ll anss=1;
    while(b){
        if(b&1) anss=anss*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return anss;
}
ll q_mul(ll a,ll b,ll mod){
    ll anss=0;
    while(b){
        if(b&1) anss=(anss+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return anss;
}
struct node{
    int v,next;
}edge[N];
int cnt,fir[N],vis1[N],a[N];
void init(){
    cnt=0;
    mef(fir);
}
void add(int u,int v){
    edge[cnt].v=v;
    edge[cnt].next=fir[u];
    fir[u]=cnt++;
}
int bfs(int x,int y){
    int vis[N],ans1=x,ans2=0;
    me0(vis);
    queue<pair<int,int> > q;
    q.push(make_pair(x,0));
    vis[x]=1;
    while(!q.empty()){
        int lx=q.front().first,ly=q.front().second;
        if(vis1[lx]){
            ans1=lx;
            ans2=ly;
        }
        q.pop();
        for(int i=fir[lx];i!=-1;i=edge[i].next){
            int lv=edge[i].v;
            if(!vis[lv]){
                q.push(make_pair(lv,ly+1));
                vis[lv]=1;
            }
        }
    }
    if(!y) return ans1;
    else return ans2;
}
int main(int argc, char * argv[]){
    ios::sync_with_stdio(false);
    int n,k;
    cin>>n>>k;
    init();

    for(int i=1;i<=n-1;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
        add(b,a);
    }
    me0(vis1);
    for(int i=1;i<=k;i++){
        cin>>a[i];
        vis1[a[i]]=1;
    }
    int re=bfs(a[1],0);
    re=bfs(re,1);
    cout<<re/2+re%2<<endl;
    return 0;
}

B题 https://ac.nowcoder.com/acm/contest/884/B

 好像说是线段树加线性基的交,还不会,先码着,学了来补

C题 https://ac.nowcoder.com/acm/contest/884/C   

题意:给定两个长度为n的数组a和b,求a数组任意一区间的最小值乘b数组相同区间和的最大值是多少。其中a,b包含负数(重点)。

题解:和南昌网络邀请赛题目一样,只不过是用前缀和存b数组就是了。

一开始时限1s会卡线段树,后面抬一手时限放了3s线段树也能过了。

官方题解:

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int dir[8][2]={{1,0},{0,1},{1,1},{1,-1},{-1,1},{-1,-1},{0,-1},{-1,0}};
#define pi acos(-1)
#define ls rt<<1
#define rs rt<<1|1
#define inf 1e18
#define me0(s) memset(s,0,sizeof(s))
#define me1(s) memset(s,1,sizeof(s))
#define mef(s) memset(s,-1,sizeof(s))
#define meinf(s) memset(s,inf,sizeof(s))
const int N=3e6+5;
ll a[N],b[N],sum[N],Min[N<<2],Max[N<<2];
stack <ll> s;
void pushup(int rt){
    Max[rt]=max(Max[ls],Max[rs]);
    Min[rt]=min(Min[ls],Min[rs]);
}
void build(int l,int r,int rt){
    if(l==r){
        Max[rt]=Min[rt]=sum[l];
        return ;
    }
    int m=(l+r)>>1;
    build(l,m,ls);
    build(m+1,r,rs);
    pushup(rt);
}
ll query_max(int L,int R,int l,int r,int rt){
    if(l<=L&&r>=R){
        return Max[rt];
    }
    ll ans=-inf;
    int m=(L+R)>>1;
    if(l<=m) ans=max(ans,query_max(L,m,l,r,ls));
    if(r>m) ans=max(ans,query_max(m+1,R,l,r,rs));
    return ans;
}
ll query_min(int L,int R,int l,int r,int rt){
    if(l<=L&&r>=R){
        return Min[rt];
    }
    ll ans=inf;
    int m=(L+R)>>1;
    if(l<=m) ans=min(ans,query_min(L,m,l,r,ls));
    if(r>m) ans=min(ans,query_min(m+1,R,l,r,rs));
    return ans; 
}
int n;
int l[N],r[N];
int main(int argc, char * argv[]) 
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        cin>>b[i];
        sum[i]=sum[i-1]+b[i];
    }
    build(1,n,1); //线段树存的是区间和的值
    for(int i=1;i<=n;i++){
        while(s.size()&&a[s.top()]>=a[i]) s.pop();
        if(s.size()) l[i]=s.top()+1;
        else l[i]=1;
        s.push(i);
    }
    while(s.size()) s.pop();
    for(int i=n;i>=1;i--){
        while(s.size()&&a[s.top()]>=a[i]) s.pop();
        if(s.size()) r[i]=s.top()-1;
        else r[i]=n;
        s.push(i);
    } //左右第一个比他大的地方
    ll ans=-inf;
    for(int i=1;i<=n;i++){
        if(a[i]>=0) ans=max(ans,(sum[r[i]]-sum[l[i]-1])*a[i]);
        else{
            ll maxn=query_max(1,n,max(l[i]-1,1),max(i-1,1),1);
            if(l[i]==1&&maxn<0) maxn=0;
            ll minn=query_min(1,n,i,r[i],1);
            ans=max(ans,(minn-maxn)*a[i]);
        } 
    }
    cout<<ans<<endl;
    return 0;
}

D题 https://ac.nowcoder.com/acm/contest/884/D

题意:给定一个数a,要求构造最少个数的3的倍数使得这些数的按位或的和为a。

官方题解:

 代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int dir[8][2]={{1,0},{0,1},{1,1},{1,-1},{-1,1},{-1,-1},{0,-1},{-1,0}};
#define pi acos(-1)
#define ls rt<<1
#define rs rt<<1|1
#define me0(s) memset(s,0,sizeof(s))
#define me1(s) memset(s,1,sizeof(s))
#define mef(s) memset(s,-1,sizeof(s))
#define meinf(s) memset(s,inf,sizeof(s))
#define inf 1e18
const int N=105;
ll f1[N],f2[N];
int a[N];
int main(int argc, char * argv[]) 
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--){
        ll n;
        cin>>n;
        if(n%3==0) cout<<1<<" "<<n<<endl;
        int len=0;
        ll m=n;
        while(m){
            a[++len]=m%2;
            m/=2;
        }
        ll x=1;
        int ans1=0;
        int ans2=0;
        for(int i=1;i<=len;i++){
            if(a[i]){
                if(x%3==1){
                    f1[++ans1]=x;
                }
                else if(x%3==2){
                    f2[++ans2]=x;
                }
            }
            x=x*2;
        }
        if(n%3==1){
            if(ans1>=2){
                cout<<2<<" ";
                cout<<n-f1[1]<<" "<<n-f1[2]<<endl;
            }
            else if(ans1==1){
                cout<<2<<" ";
                cout<<n-f1[1]<<" "<<f1[1]+f2[1]<<endl;
            }
            else if(ans2>=3){
                cout<<2<<" ";
                cout<<f2[1]+f2[2]+f2[3]<<" "<<n-f2[1]-f2[2]<<endl;
            }
        }
         else if(n%3==2){
             if(ans2>=2){
                cout<<2<<" ";
                cout<<n-f2[1]<<" "<<n-f2[2]<<endl;
            }
            else if(ans2==1){
                cout<<2<<" ";
                cout<<n-f2[1]<<" "<<f2[1]+f1[1]<<endl;
            }
            else if(ans1>=3){
                cout<<2<<" ";
                cout<<f1[1]+f1[2]+f1[3]<<" "<<n-f1[1]-f1[2]<<endl;
            }
         }
    }
    return 0;
}

J题 https://ac.nowcoder.com/acm/contest/884/J

题意:n个点m条边,从点s到t的路径中,选择最短路,其中你可以将任意k条边的花费变为0,问最小花费是多少

题解:最短路,队友拍的,不懂。

官方题解:

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
#define LL long long
const int MOD=1e9+7;
const int inf=0x3f3f3f3f;
const LL inff=0x3f3f3f3f3f3f3f3f;
const LL MAX_N=10005;
const LL MAX_M=100005;
#define MEF(x) memset(x,-1,sizeof(x))
#define ME0(x) memset(x,0,sizeof(x))
#define MEI(x) memset(x,inf,sizeof(x))
struct Node
{
    LL i,k,dis;
 
    bool operator <(const Node &a)const
    {
        return dis>a.dis;
 
    }
};
struct Edge
{
    LL v,w,next;
}edge[MAX_M];
LL dis[MAX_N][15];
 
LL first[MAX_N],cnt,vis[MAX_N][15];
void add(LL u,LL v,LL w)
{
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].next=first[u];
    first[u]=cnt++;
}
void dijstra(LL s,LL t,LL k)
{
    priority_queue<Node> pq;
    MEI(dis);
    ME0(vis);
    Node l;
    l.i=s;
    l.k=0;
    l.dis=0;
 
    dis[s][0]=0;
 
    pq.push(l);
    while(!pq.empty())
    {
        l=pq.top();
        pq.pop();
        LL li=l.i,lk=l.k;
        if(vis[li][lk])
        {
            continue;
        }
        vis[li][lk]=1;
        for(LL i=first[li];i!=-1;i=edge[i].next)
        {
            LL lv=edge[i].v;
            if(!vis[lv][lk]&&dis[lv][lk]>dis[li][lk]+edge[i].w)
 
            {
                dis[lv][lk]=dis[li][lk]+edge[i].w;
 
                l.i=lv;
                l.k=lk;
                l.dis=dis[lv][lk];
 
                pq.push(l);
            }
            if(lk+1<=k&&!vis[lv][lk+1]&&dis[lv][lk+1]>dis[li][lk])
 
            {
                dis[lv][lk+1]=dis[li][lk];
 
                l.i=lv;
                l.k=lk+1;
                l.dis=dis[lv][lk+1];
 
                pq.push(l);
            }
        }
    }
    LL ans=dis[t][k];
    for(int i=0;i<=k;i++)
    {
        ans=min(dis[t][i],ans);
    }
    printf("%lld
",ans);
}
int main()
{
    LL n,m,k,st,ed,a,b,c;
    scanf("%lld%lld%lld%lld%lld",&n,&m,&st,&ed,&k);
    cnt=0;
    MEF(first);
    for(LL m1=1;m1<=m;m1++)
    {
        scanf("%lld%lld%lld",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    dijstra(st,ed,k);
    return 0;
}

K题: https://ac.nowcoder.com/acm/contest/884/K

题意:给定一串字符,求字符子串能构成300倍数出现的次数,0,00,000以及前导零都算。

题解:比赛时候刷刷刷的过人,我们辛辛苦苦暴力还超时,自闭。(万物皆可dp)

官方题解:

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int dir[8][2]={{1,0},{0,1},{1,1},{1,-1},{-1,1},{-1,-1},{0,-1},{-1,0}};
#define pi acos(-1)
#define ls rt<<1
#define rs rt<<1|1
#define me0(s) memset(s,0,sizeof(s))
#define me1(s) memset(s,1,sizeof(s))
#define mef(s) memset(s,-1,sizeof(s))
#define meinf(s) memset(s,inf,sizeof(s))
#define inf 1e18
const int N=1e6+6;
inline int read() {
    char c=getchar(); int x=0, f=1;
    while(c<'0'|c>'9') {if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
ll exgcd(ll a,ll b){
    if(b==0) return a;
    exgcd(b,a%b);
}
ll q_pow(ll a,ll b,ll mod){
    ll anss=1;
    while(b){
        if(b&1) anss=anss*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return anss;
}
ll q_mul(ll a,ll b,ll mod){
    ll anss=0;
    while(b){
        if(b&1) anss=(anss+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return anss;
}
int a[N];
int main(int argc, char * argv[]) 
{
    ios::sync_with_stdio(false);
    string s;
    cin>>s;
    int sum=0;
    ll ans=0;
    a[0]++;
    for(int i=0;i<s.length();i++){
        sum+=s[i]-'0';
        sum%=3;
        if(s[i+1]=='0'&&s[i+2]=='0') ans+=a[sum];
        a[sum]++;
    }
    for(int i=0;i<s.length();i++){
        if(s[i]=='0') ans++;
        if(s[i]=='0'&&s[i+1]=='0') ans++;
    }
    cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/wushengyang/p/11262392.html