2019牛客多校第四场 A,B,C,D,E,J,K

A-meeting

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

题目大意:n个点的树。k个人分别在点Xk,求出所有人都到一个点的最短花费时间。

思路:将所有多余的树枝都砍掉,两次DFS。剩下的都是有用的。求剩下的这棵树的直径,因为直径上的两端点必定是最长的时间花费,因此两次DFS求出直径后,直接计算直径即可。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
  
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
  
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
  
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
 
struct Node{
    int v,val,nxt;
    Node(int _v=0,int _val=0,int _nxt=0){
        v=_v;val=_val;nxt=_nxt;
    }
};
Node Edge[MAXN<<2];
int Head[MAXN],Ecnt;
int Deep[MAXN],Val[MAXN],Sum[MAXN],Vis[MAXN];
int res,ans,n,m;
 
void Intt(){
    clean(Head,-1);Ecnt=0;
    clean(Deep,0);clean(Val,0);clean(Sum,0);
}
void AddEdge(int u,int v,int val){
    Edge[Ecnt]=Node(v,val,Head[u]);
    Head[u]=Ecnt++;
}
void DFS(int u,int fa,int dep=0){
    Vis[u]=1;
    Deep[u]=dep;
    Sum[u]=Val[u];
    for(int i=Head[u];i+1;i=Edge[i].nxt){
        int v=Edge[i].v;
        if(v==fa) continue;
        if(Edge[i].val==0) continue;
        DFS(v,u,dep+1);Sum[u]+=Sum[v];
        if(Sum[v]==0){//子节点没有贡献 ,砍掉
            Edge[i].val=0;
        }
    }
}
int main(){
    Intt();
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;++i){
        int u,v;scanf("%d%d",&u,&v);
        AddEdge(u,v,1);AddEdge(v,u,1);
    }
    for(int i=1;i<=m;++i){
        int x;scanf("%d",&x);Val[x]=1;
    }DFS(1,0);//砍树
    int rt=1;
    clean(Vis,0);DFS(rt,0);//标记。
    for(int i=1;i<=n;++i){//找根
        if(Vis[i]&&Deep[i]>Deep[rt]) rt=i;
    }
//  printf("rt=%d\n",rt);
    clean(Sum,0);clean(Deep,0);clean(Vis,0);
    DFS(rt,0);//换根继续砍
    clean(Deep,0);
    DFS(rt,0); //求深度
    int d=0;
    for(int i=1;i<=n;++i) d=max(d,Deep[i]);
//  printf("d=%d\n",d);
    int Ans=d%2?d/2+1:d/2;
    printf("%d\n",Ans);
     
}

B-xor

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

数论队友线性基:https://ac.nowcoder.com/acm/contest/884/B

C-sequence

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

题目大意:给出两个序列a,b,找出找出最大的值:1≤l≤r≤n max​{min(al…r​)×sum(bl…r​)}

思路:有点类似上半年南昌邀请网络赛https://blog.csdn.net/qq_40482358/article/details/89422999

枚举最小的a[i],确定a[i]的最小区间范围。找到范围内的最大的前缀和和最小的前缀和即可。

a[i]的最小的范围可以用单调栈来维护,从前到后和从后到前分别维护单增的栈。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
  
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
  
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
  
const int MAXN=3e6+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
 
struct SegTree{
    ll Max[MAXN<<2],Min[MAXN<<2];
     
    void Build(int l,int r,int rt,ll A[]){
        if(l==r){
            Max[rt]=Min[rt]=A[l];
            return ;
        }
        int mid=(l+r)>>1;
        Build(l,mid,rt<<1,A);Build(mid+1,r,rt<<1|1,A);
        Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
        Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
    }
    ll QueryMax(int ql,int qr,int l,int r,int rt){
        if(ql<=l&&r<=qr) return Max[rt];
        int mid=(l+r)>>1;
        ll res=-INF64;
        if(ql<=mid) res=max(res,QueryMax(ql,qr,l,mid,rt<<1));
        if(qr>mid) res=max(res,QueryMax(ql,qr,mid+1,r,rt<<1|1));
        return res;
    }
    ll QueryMin(int ql,int qr,int l,int r,int rt){
        if(ql<=l&&r<=qr) return Min[rt];
        int mid=(l+r)>>1;
        ll res=INF64;
        if(ql<=mid) res=min(res,QueryMin(ql,qr,l,mid,rt<<1));
        if(qr>mid) res=min(res,QueryMin(ql,qr,mid+1,r,rt<<1|1));
        return res;
    }
};
SegTree Seg;//,SegMin;
ll A[MAXN],B[MAXN];
ll Min[MAXN][2],SumB[MAXN];
int Stk[MAXN],Index[MAXN],top;
int n;
 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%lld",&A[i]);
    for(int i=1;i<=n;++i) scanf("%lld",&B[i]);
    top=0;
    for(int i=1;i<=n;++i){
        while(top>0&&A[i]<Stk[top]) --top;
        if(top==0) Min[i][0]=1;
        else Min[i][0]=Index[top]+1;
        Stk[++top]=A[i];Index[top]=i;
    }
    top=0;
    for(int i=n;i>=1;--i){
        while(top>0&&A[i]<Stk[top]) --top;
        if(top==0) Min[i][1]=n;
        else Min[i][1]=Index[top]-1;
        Stk[++top]=A[i];Index[top]=i;
    }
    for(int i=2;i<=n+1;++i) SumB[i]=SumB[i-1]+B[i-1];SumB[1]=0;
    Seg.Build(1,n+1,1,SumB);
    ll Ans=-INF64;
    for(int i=1;i<=n;++i){//对于每个点,求出他的影响区间,如果该点为负数,左右找打最小的前缀和,正数找到最大的Sum
        int l=Min[i][0],r=Min[i][1];
        l++;r++;//区间向后移动一位 
        l--;//查询区间[l-1,i][i,r]; 
        ll Suml,Sumr;
        if(A[i]<0){
            Suml=Seg.QueryMax(l,i+1,1,n+1,1);
            Sumr=Seg.QueryMin(i+1,r,1,n+1,1);
        }
        else{
            Suml=Seg.QueryMin(l,i+1,1,n+1,1);
            Sumr=Seg.QueryMax(i+1,r,1,n+1,1);
        }
        //printf("l=%d r=%d\n",l,r);
        //printf("Suml=%lld Sumr=%lld\n",Suml,Sumr);
        Suml=SumB[i]-Suml;Sumr=Sumr-SumB[i];
        //printf("Suml=%lld Sumr=%lld\n",Suml,Sumr);
		Ans=max(Ans,1ll*(Suml+Sumr)*A[i]);
        //printf("Ans=%lld\n",Ans);
    }printf("%lld\n",Ans);
}

D-triples I

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

数论队友:https://blog.csdn.net/henucm/article/details/97758303

E-triples II

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

数论队友:https://blog.csdn.net/henucm/article/details/102639665

J-free

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

题目大意:一个图,可以走k次近道。稳到达终点的最小花费。

思路:分层最短路板子题

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
  
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
  
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
  
const int MAXN=1e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
 
 
struct Node{
    int v,val,nxt;
    Node(int _v=0,int _val=0,int _nxt=0){
        v=_v;val=_val;nxt=_nxt;
    }
};
Node Edge[MAXN<<2];
int Head[MAXN],Ecnt;
int Vis[MAXN][MAXN],Dis[MAXN][MAXN];
int n,m,S,T,k;
 
void Intt(){
    clean(Head,-1);Ecnt=0;
    clean(Vis,0);clean(Dis,INF32);
}
void AddEdge(int u,int v,int val){
    Edge[Ecnt]=Node(v,val,Head[u]);
    Head[u]=Ecnt++;
}
#define Piii pair<int,Pair>
#define M_P(a,b,c) make_pair(a,make_pair(b,c))
void Dijkstra(){
    Dis[S][0]=0;
    priority_queue<Piii> que;que.push(M_P(0,S,0));
    while(que.size()){
        while(Vis[que.top().second.first][que.top().second.second]&&que.size()>0) que.pop();
        Pair p=que.top().second;
        Vis[p.first][p.second]=1;
        for(int i=Head[p.first];i+1;i=Edge[i].nxt){//同层查找
            int v=Edge[i].v;
            if(Vis[v][p.second]==0&&Dis[v][p.second]>Dis[p.first][p.second]+Edge[i].val){
                Dis[v][p.second]=Dis[p.first][p.second]+Edge[i].val;
                que.push(M_P(-Dis[v][p.second],v,p.second));
            }
            if(p.second+1<=k&&Vis[v][p.second+1]==0&&Dis[v][p.second+1]>Dis[p.first][p.second]){
                Dis[v][p.second+1]=Dis[p.first][p.second];
                que.push(M_P(-Dis[v][p.second+1],v,p.second+1));
            }
        }
    }
}
int main(){
    Intt();
    scanf("%d%d%d%d%d",&n,&m,&S,&T,&k);
    for(int i=1;i<=m;++i){
        int u,v,val;scanf("%d%d%d",&u,&v,&val);
        AddEdge(u,v,val);AddEdge(v,u,val);
    }Dijkstra();
    int Ans=INF32;
    for(int i=0;i<=k;++i){
        Ans=min(Ans,Dis[T][i]);
    }printf("%d\n",Ans);
}

K-number

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

题目大意:给出一串数字,判断有多少个字符串是300的倍数。

思路:求出前缀之和%3的余数。求出对应的个数。从后向前遍历字符串,cnt表示连续0的个数。

当出现一个不为0的数时,判断该位前面有多少个相同的数(相同的数表示中间相差的有3的倍数)加上后面0的个数组成数量。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
  
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
  
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
  
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
 
char S[MAXN];
int Sum[MAXN];
int A[4][MAXN];
 
int main(){
    scanf("%s",S+1);
    int len=strlen(S+1);//printf("len=%d\n",len);
    for(int i=1;i<=len;++i){
        Sum[i]=Sum[i-1]+S[i]-'0';
        Sum[i]%=3;
    }
    A[0][0]++;
    for(int i=1;i<=len;++i){
        for(int j=0;j<=2;++j) A[j][i]=A[j][i-1];
        A[Sum[i]][i]++;
    }
//  for(int i=0;i<=2;++i){
//      for(int j=0;j<=len;++j){
//          printf(" %d",A[i][j]);
//      }printf("\n");
//  }
    ll Ans=0;
    int cnt=0;
    for(int i=len;i>=0;--i){
        if(S[i]=='0') cnt++;
        else{
            if(cnt>=2){//Ans+
                int x=Sum[i];
                int num=A[x][i];
                num-=1;
                Ans+=1ll*(cnt-1)*num;
                Ans+=(1ll+cnt)*cnt/2;
            }
            if(cnt==1) Ans++;
            cnt=0;
        }
//      printf("i=%d Ans=%lld\n",i,Ans);
    }printf("%lld\n",Ans);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值