2019暑期训练-牛客第四场

K:number:

签到水题,略。

A:meeting

题意
在一个树上有m个人,每一秒每个人可以移动一条边。求这m个人移动到同一点所花费的最小时间。
思路
只需要找m个人中,路径最远的两个人的距离的一半儿就可以了。两边bfs或者dfs求d[]数组即可。时间复杂度:O(n+m)
AC代码

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
#define pb push_back
#define _fileout freopen("out.txt","w",stdout)
#define _filein freopen("in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret = 1;while(b){if (b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return (a*b)/gcd(a,b);}
int d[MAXN];
int vis[MAXN];
int a[MAXN];
int head[MAXN],e[MAXN],nexts[MAXN];
int o;
void add(int x,int y)
{
    e[++o]=y;
    nexts[o]=head[x];head[x]=o;
}
void bfs(int x)
{
    // memset(d,0,sizeof(d));
    queue<int>q;
    q.push(x);
    d[x]=0;
    while(!q.empty())
    {
        x=q.front();
        q.pop();
        if(vis[x])continue;
        vis[x]=1;
        for(int i=head[x];i;i=nexts[i])
        {
            int y=e[i];
            if(vis[y])continue;
            d[y]=d[x]+1;
            q.push(y);
        }
    }
    return ;
}
int main(void)
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&a[i]);
    }
    bfs(a[1]);
    int x=a[1];
    // printf("%d\n",x);
    for(int i=1;i<=m;i++)
    {
        if(d[a[i]]>d[x])x=a[i];
    }
    // for(int i=1;i<=n;i++)printf("d[%d]=%d\n",i,d[i]);
    for(int i=1;i<=n;i++)vis[i]=0;;
    bfs(x);
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        ans=max(ans,d[a[i]]);
    }
    // for(int i=1;i<=n;i++)printf("d[%d]=%d\n",i,d[i]);
    printf("%d",(ans+1)/2);
    return 0;
}   

J:free

题意:
给一张图,可以把k条边的权值变为0,求从起点到终点的最短路径。
思路:
在djstla找最短路的算法中,d数组开两维,第一维是点,第二维是把几条边的权值变成了0,直接跑一边最短路即可。由于加了剪枝,时间复杂度:O(n*k)。
AC代码:

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
#define pb push_back
#define _fileout freopen("out.txt","w",stdout)
#define _filein freopen("in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e4+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret = 1;while(b){if (b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return (a*b)/gcd(a,b);}
struct state
{
    int x,k,w;
    bool operator <(const state &a)const
    {
        return w>a.w;
    } 
};
ll d[MAXN][MAXN];
int head[MAXN],nexts[MAXN],e[MAXN],w[MAXN];
int o;
void add(int x,int y,int z)
{
    e[++o]=y;w[o]=z;nexts[o]=head[x];head[x]=o;
}
int n,m,s,t,k;
ll djstla()
{
    for(int i=0;i<=n;i++)
        for(int j=0;j<=k;j++)
            d[i][j]=INF;
    for(int i=0;i<=k;i++)d[s][i]=0;
    priority_queue<state>q;
    state now,next;
    now.x=s;now.k=0;now.w=0;
    q.push(now);
    while(!q.empty())
    {
        now=q.top();
        q.pop();
        // if(vis[now.x])continue;
        // vis[now.x]=1;
        if(now.w!=d[now.x][now.k])continue;//目前队列中存在更优的状态
        if(now.x==t)return d[now.x][now.k];
        for(int i=head[now.x];i;i=nexts[i])
        {
            int y=e[i];
            if(d[y][now.k]>d[now.x][now.k]+w[i]){
                d[y][now.k]=d[now.x][now.k]+w[i];
                next.x=y;next.w=d[y][now.k];next.k=now.k;
                q.push(next);
            }
            if(now.k<k&&d[y][now.k+1]>d[now.x][now.k]){
                d[y][now.k+1]=d[now.x][now.k];
                next.x=y;next.w=d[now.x][now.k];next.k=now.k+1;
                q.push(next);
            }
        }


    }
}
int main(void)
{
    scanf("%d%d%d%d%d",&n,&m,&s,&t,&k);
    for(int i=0;i<m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    printf("%lld",djstla());

    return 0;
}   

D: triples I

题意:
给定一个数a,求一个各个元素都是3的倍数的数组,使各元素进行或操作之后的结果是a。输出最小长度的符合要求的数组。
思路:
考虑到a的二进制状态,以及在二进制中,奇数(下标从1开始)为上的1膜3之后的余数是1,偶数位上的1膜3之后的余数是2。所以直接根据给的a进行构造即可。可以发现最多只需要两个数即可。需要分类讨论,因为a的情况很多。比如如果a%3=1,那么需要构造这两个数一个数需要去掉a的奇数位上的一个1,另外一个数需要去掉四个,或者去掉两个偶数位上的1。需要对a的奇数位和偶数位上的1的数量进行讨论。
代码:

//略

C:sequence

题意: 给a【】,b【】两个数组,求一个区间 l~r 使min(a[l] ~ a[r]) * sum(b[l] ~ b[r]) 的值最大。输出最大值。
思路: (不知道和题解是不是一样)
枚举每个以a[i]为最小值的最大区间 l~r,然后对于b数组,如果a[i]>0,先找到 b数组中i ~ r的区间前缀和的最大值ma,再找 l ~ i 的区间前缀和的最小值mi,然后用 a[i]*(ma-mi)更新答案。如果 a[i]<0 ,那么对b数组的操作相反即可。
对于a数组可以用单调栈的方法求l和r。对于b数组,维护前缀和的区间最大值的线段树。
AC代码:

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
#define pb push_back
#define _fileout freopen("out.txt","w",stdout)
#define _filein freopen("in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e7+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret = 1;while(b){if (b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return (a*b)/gcd(a,b);}
struct NODE
{
    int num;
    int l;
    int r;
}a[MAXN];
struct segment_tree
{
    int l,r;
    ll w[2];//sum数组区间最大值与最小值。0表示最大值,1表示最小值
}t[MAXN];
ll b[MAXN];
int n;
void update_a()//单调栈求以a[i]为最小值的最大区间
{
    int i,j;
    stack<int> monostack;
    for(i=1;i<=n;i++)
    {
        while(!monostack.empty() && a[monostack.top()].num > a[i].num)
        {
            a[monostack.top()].r=i-1;
            a[i].l=a[monostack.top()].l;
            monostack.pop();
        }
        monostack.push(i);
    }
    if(!monostack.empty())
        j=monostack.top();
    while(!monostack.empty())
    {
        a[monostack.top()].r=j;
        monostack.pop();
    }
}
void build(int l,int r,int p)//建立b数组前缀和的线段树
{
    t[p].l=l;
    t[p].r=r;
    if(l==r){t[p].w[0]=t[p].w[1]=b[l];return;}
    int mid=(l+r)/2;
    build(l,mid,p*2);
    build(mid+1,r,p*2+1);
    t[p].w[0]=max(t[p*2].w[0],t[p*2+1].w[0]);
    t[p].w[1]=min(t[p*2].w[1],t[p*2+1].w[1]);
    return;
}
ll ask(int l,int r,int p,int f)//查询最大值或者最小值
{
    // printf("%d %d %d\n",l,r,p);
    ll ans;
    if(f==0)ans=-ll_INF;
    else ans=ll_INF;
    if(l<=t[p].l&&r>=t[p].r)return t[p].w[f];
    int mid=(t[p].l+t[p].r)/2;
    if(f==0)
    {
        if(l<=mid)ans=max(ans,ask(l,r,p*2,f));
        if(r>mid)ans=max(ans,ask(l,r,p*2+1,f));
    }
    else 
    {
        if(l<=mid)ans=min(ans,ask(l,r,p*2,f));
        if(r>mid)ans=min(ans,ask(l,r,p*2+1,f));
    }
    return ans;
}
int main(void)
{
    ll ans=-INF;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].num);
        a[i].l=a[i].r=i;
    }
    for(int i=1;i<=n;i++)
    {
        int mid;
        scanf("%d",&mid);
        ans=max(ans,(ll)a[i].num*(ll)mid);
        b[i]=b[i-1]+(ll)mid;//b数组就是题目b数组的前缀和
    }
    update_a();
    build(1,n,1);
    // ok(1);
    // for(int i=1;i<=n;i++)
    // {
    //     cout<<a[i].num<<":"<<a[i].l<<" "<<a[i].r<<endl;
    // }
    for(int i=1;i<=n;i++)
    {
        if(a[i].num==0)ans=max(ans,0ll);
        else if(a[i].num<0)
        {
            // ok(1);
            ll mi=ask(i,a[i].r,1,1);
            ll ma=ask(a[i].l,i,1,0);
            // ok(2);
            ans=max(ans,(ll)a[i].num*(mi-ma));
        }
        else
        {
            // ok(3);
            ll ma=ask(i,a[i].r,1,0);
            // ok(4);
            ll mi=ask(a[i].l,i,1,1);
            ans=max(ans,(ll)a[i].num*(ma-mi));
        }
    }
    printf("%lld",ans);
    return 0;
}   

附:牛客网的oj很迷,数据量开够不是报T就是段错误。非要把数据量开到题意的10倍才会AC。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值