2019牛客暑期多校训练营(四)

声明:本文章题目来源均为牛客网

链接:https://ac.nowcoder.com/acm/contest/884#question

A-meeting

题目类型:DFS、BFS

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

题目大意

给n个城市,城市与城市之间一共有n-1条边相连(就是一颗n个结点的树),然后有k个在不同城市的人(取树上的k个结点),他们决定聚在某一个城市,每走一条路所需要的时间为1,求这k个人聚到一起需要的最短时间。(n<105

题目理解

首先距离最长的两个点肯定是最后聚在一起的,而且相聚点在这条距离最长的路中间。

求这条最长路可以先由任意一点进行DFS或BFS找出一条伪最长路,然后再由这条伪最长路的另一个端点再进行一次DFS或者BFS找到所需要的最长路,也就是找树的直径。

证明:如下图

图中AO<BO,CO<DO,BO<DO

AO+DO<DO+OB

设A点为选取的任意一点,进行DFS遍历,得到AD为伪最长路,那么再由B点DFS则BD为最长路(树的直径)。

于是此题可用两次DFS解决,最后输出(最长路+1)/2(较长的一半为最短时间)。

AC代码

#include<iostream>
#include<vector>
using namespace std;
vector<int> v[100010];
int loc[100010],ans=0,res;
void dfs(int now,int p,int len){
    if(loc[now]&&len>ans){
        ans=len;
        res=now;
    }
    int y=v[now].size();
    for(int i=0;i<y;i++){
        int next=v[now][i];
        if(next==p) continue;
        dfs(next,now,len+1);
    }
}
int main(){
    int n,k;
    cin >> n >> k;
    for(int i=0;i<n-1;i++){
        int v1,v2;
        cin >> v1 >> v2;
        v[v1].push_back(v2);
        v[v2].push_back(v1);    
    }
    while(k--){
        int x;
        cin >> x;
        loc[x]=1;
    }
    dfs(1,1,0);
    dfs(res,res,0);
    cout << (ans+1)/2;
    return 0;
}

后记

题目考察树的直径,平时做树的题目较少,而且比赛的时候没有仔细读题与思考,导致签到题都没做。

B-xor

爆肝中~

C-sequence

题目类型:分治、线段树

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

题目大意

有两个有n个元素的数组a和数组b,求一个[ l,r ]的区间,使得a数组元素在此区间的最小值乘上b数组在此区间的元素之和所得的数最大。

(n < 3e6,-1e< ( ai,bi ) < 1e6)

D-triples I

题目类型:按位或运算,思维

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

题目大意

输入T个数,每个数都能由3的倍数按位或得来,对于输入的数a,输出任意一组3的倍数,这些数的按位或的结果等于a。(1 < T < 1e5,1 < a < 1e18

题目理解

一个简单的数字构造题。对于一个数a,a模上3的结果只可能为0、1、2。

如果为0,则输出a即可;

如果不为0,则将其转化为二进制。比如样例中的7,将7写成二进制,为111,其二进制每一位代表的数字模3为121(4%3=1,2%3=2,1%3=1),

得规律:如果二进制位1所在位置为奇数位,则模3为1,偶数位则为2。

  • 如果a%3=1。如果a的二进制有两个及以上的奇数位的1,假设b为一个奇数位的1代表的数字,再假设c为另一个奇数位的1代表的数字,按照上面所讲的规律,a-b和a-c都为3的倍数,根据按位或的性质(只要为1则为1),(a-b) | (a-c) = a。如果二进制只有一个奇数位的1,设b为这个1代表的数字,c为任意一个偶数位1所代表的数字,a-b和b+c都为3的倍数,且(a-b) | (b+c) = a。如果二进制没有奇数位的1,设b,c,d为任意3个不同的偶数位所代表的数字,a-b-c和a+b+c为3的倍数,且(a-b-c) | (b+c+d)
  • 如果a%3=2。原理和上面一样,只需要将每一步的奇偶性反过来就行了,这里就不说了。

总结:就用原来的数字a构造3的倍数,去掉模3的有余数的数就行了,而且联想一下可以发现,只需要最多两个3的倍数就可以按位或成数字a。

AC代码

#include<iostream>
#include<vector>
#define sc(x) scanf("%d",&x);
#define scll(x) scanf("%lld",&x);
using namespace std;
typedef long long ll;
int main(){
    int t;sc(t);
    while(t--){
        ll n;scll(n);
        vector<int> one,two;
        for(int i=0;i<62;i++)
            if((n>>i)&1){//用vector容器分别保存二进制位奇偶性不同的1的位置 
                if(i&1) two.push_back(i); //保存偶数位1 
                else one.push_back(i);//保存奇数位1 
            } 
        if(n%3==0) printf("1 %lld\n",n);
        else if(n%3==1){
            if(one.size()>=2) 
                printf("2 %lld %lld\n",n-(1LL<<one[0]),n-(1LL<<one[1]));
            else if(one.size()==1)  
                printf("2 %lld %lld\n",n-(1LL<<one[0]),(1LL<<two[0])+(1LL<<one[0]));
            else 
                printf("2 %lld %lld\n",n-(1LL<<two[0])-(1LL<<two[1]),(1LL<<two[0])+(1LL<<two[1])+(1LL<<two[2]));
        }else if(n%3==2){
            if(two.size()>=2) 
                printf("2 %lld %lld\n",n-(1LL<<two[0]),n-(1LL<<two[1]));
            else if(two.size()==1) 
                printf("2 %lld %lld\n",n-(1LL<<two[0]),(1LL<<two[0])+(1LL<<one[0]));
            else 
                printf("2 %lld %lld\n",n-(1LL<<one[0])-(1LL<<one[1]),(1LL<<one[0])+(1LL<<one[1])+(1LL<<one[2]));
        }
    }
    return 0;
}

反思

构造题,找规律,也要看自己对位运算的理解以及运用。

j-free

题目类型:分层图最短路

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

题目大意

有一个非直接连接的图,n个结点,m条边,每条边有花费l,要求在可以让k条边权值为0的情况下,求s点到t点的最小花费。

(1 <= n,m <= 1e3,1 <= s,t <= n,1 <= l <= 1e6

题目理解

一道分层图最短路模板题,把保存花费的d数组多开一维记录k次机会的信息。d[ i ][ j ]达到 i 点用了 j 次机会的最小花费。

更新信息的时候先更新同一层的(用了相同次机会)最小花费,然后再更新下一层的(再用一次机会)最小花费。

dijkstra最短路就行了(用优先队列维护)。

AC代码

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m,s,t,k,d[1010][1010];
typedef pair<int,int> P;
struct node{
    int to,cost;
    node(int a,int b){
        to=a;
        cost=b;
    }
};
vector<node> v[1010];
void dijkstra(int s){ 
    memset(d,inf,sizeof(d));
    d[s][0]=0;
    queue<P> que;
    que.push(P(s,0));
    while(!que.empty()){
        P now=que.front();
        que.pop();
        int u=now.first,t=now.second;
        for(int i=0;i<v[u].size();i++){
            int x=v[u][i].to;
            int pay=v[u][i].cost;
            if(d[x][t]>d[u][t]+pay) //更新同层的最小花费 
                d[x][t]=d[u][t]+pay,que.push(P(x,t));
            if(t<k&&d[x][t+1]>d[u][t]) //更新下一层的最小花费 
                d[x][t+1]=d[u][t],que.push(P(x,t+1));
        }
    }
}
int main(){
    cin >> n >> m >> s >> t >> k;
    while(m--){
        int a,b,c;
        cin >> a >> b >> c;
        v[a].push_back(node(b,c));
        v[b].push_back(node(a,c));
    } 
    dijkstra(s);
    int ans=inf;
    for(int i=0;i<=k;i++)
        ans=min(ans,d[t][i]);
    printf("%d",ans);
    return 0;
} 

后记

要不是出了这题我还不知道分层图这个东西,还是练习太少的锅。

猜你喜欢

转载自www.cnblogs.com/lastonepersonwhohavebitenbycompanies/p/11259203.html