2020年牛客算法入门课练习赛3 (A bfs B 容斥 C 线段树+主席树 D 暴力最短路 E 思维构造 )

昨晚 div3 A 出了 最后一题,只有100左右人 A 的题有点兴奋 玩到2点,中午没睡着,傍晚吃了一颗维生素C(助睡眠)睡了20分钟,扛着迷迷糊糊的大脑来打这场。然后就没打好,四个题都会写,就是A题找bug浪费n久。导致赛时2题,赛后半小时又两题 

A-胖胖的牛牛

2020年牛客算法入门课练习赛3 (A bfs B 容斥 C 线段树+主席树 D 暴力最短路 E 思维构造 )_第1张图片

做法:经典bfs水题了。不会的去面壁,萌新除外

#pragma GCC optimize(2)
#include
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=1e2+10;
char s[N][N];
int n;
ll vis[N][N];
int si,sj,ei,ej;
struct node
{
    int x,y;
    ll w;
    int id;
    bool operator <(const node &o)const
    {
        return w>o.w;
    }
};
int dir[4][2]={1,0,0,1,-1,0,0,-1};
void bfs()
{
    priority_queueque;
    rep(i,1,n) rep(j,1,n) vis[i][j]=1e9;vis[si][sj]=0;

    que.push({si,sj,0,-1});
    while(que.size())
    {
        node now=que.top();que.pop();

        //printf("x:%d y:%d w:%d id:%d\n",now.x,now.y,now.w,now.id);

        if(now.x==ei&&now.y==ej){printf("%lld\n",now.w);return ;}

        for(int i=0;i<4;++i){
            int x=now.x+dir[i][0];
            int y=now.y+dir[i][1];
            if(x<1||y<1||x>n||y>n||s[x][y]=='x') continue;
            if(now.id==-1||i==now.id){
                if(vis[x][y]>=now.w){
                    vis[x][y]=now.w;
                    que.push({x,y,vis[x][y],i});
                }
            }
            else{
                int w=1;
                if(abs(now.id-i)==2) w=2;

                if(vis[x][y]>=now.w+w){
                    vis[x][y]=now.w+w;
                    que.push({x,y,vis[x][y],i});
                }
            }
        }

    }
    puts("-1");
}
int main()
{
    cin>>n;
    rep(i,1,n) rep(j,1,n) {
        cin>>s[i][j];
        if(s[i][j]=='A') si=i,sj=j;
        if(s[i][j]=='B') ei=i,ej=j;
    }

    if(si==0||sj==0||ei==0||ej==0){puts("-1");return 0;}

    bfs();
//    rep(i,1,n) {
//        rep(j,1,n) printf("%d ",vis[i][j]);
//        puts("");
//    }
}
/*
6
A . x x x x
. . . . . x
x . x x . x
x . . x x x
x x . . . x
x x x x B x
*/

 

B-牛牛的零食

2020年牛客算法入门课练习赛3 (A bfs B 容斥 C 线段树+主席树 D 暴力最短路 E 思维构造 )_第2张图片

做法:数据: n只有15,容斥一下,区间内 被8整除的个数 减去 被8整除同时被其他某个数整除的数 加上.....(奇加偶减)

由于数很大,计算多个数的LCM时需要运用唯一分解的 分解素数的方法 保存素数最大次幂

#pragma GCC optimize(2)
#include

#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
ll powmod(ll a,ll b) {
    ll res=1;
    for(;b;b>>=1){
        if(b&1)res=res*a;
        a=a*a;
    }
    return res;
}
inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=20;
ll a[N],l,r,f[N];
int n,len;

vector >num[N];

ll run(ll v)
{
    //v=v/8;

    ll ans=0;
    for(int i=1;i<=len;++i){//m枚举状态
        ll res=1;
        int num1=0;
        mapmp;
        for(int j=0;jv) {
                flag=0;
                break;
            }
        }

        if(!flag) continue;
        //printf("v:%lld i:%d res:%lld\n",v,i,res);
        ll gc=gcd(res,8);
        res=res*8/gc;
        //res=res/gc;
        if(num1%2) ans+=v/res;
        else ans-=v/res;
    }
    return ans;
}
int main()
{
    n=read();
    rep(i,0,n-1) a[i]=read();

    f[0]=1;rep(i,1,n+1) f[i]=f[i-1]*2;
    rep(i,0,n-1)
    {
        ll tmp=a[i];
        for(int j=2;j*j<=tmp;++j){
            if(tmp%j==0){
                int res=0;
                while(tmp%j==0) res++,tmp=tmp/j;
                num[i].push_back({j,res});
            }
        }
        if(tmp!=1) num[i].push_back({tmp,1});

    }


    l=read(),r=read();
    len=(1<

C-牛牛的最美味和最不美味的零食

2020年牛客算法入门课练习赛3 (A bfs B 容斥 C 线段树+主席树 D 暴力最短路 E 思维构造 )_第3张图片

做法:eat部分不能暴力去写,这里用主席树维护每个位置是否有数,然后对2 操作的l r  查询下区间内第l 大   第r大的位置即可。

得到新的l、r即可。最大值就用普通线段树就可以了。

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=1e6+10,inf=1e9+10;
int sum[4*N],mx[4*N],mi[4*N],n,m;
void build(int id,int l,int r)
{
    if(l==r){
        scanf("%d",&mx[id]);
        mi[id]=mx[id];
        sum[id]=1;
        return ;
    }
    int mid=l+r>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    mx[id]=max(mx[id<<1],mx[id<<1|1]);
    mi[id]=min(mi[id<<1],mi[id<<1|1]);
    sum[id]=sum[id<<1]+sum[id<<1|1];
}
void up(int id,int l,int r,int pos)
{
    if(l==r){
        mx[id]=-inf,mi[id]=inf;sum[id]=0;
        return ;
    }
    int mid=l+r>>1;
    if(pos<=mid) up(id<<1,l,mid,pos);
    else up(id<<1|1,mid+1,r,pos);
    mx[id]=max(mx[id<<1],mx[id<<1|1]);
    mi[id]=min(mi[id<<1],mi[id<<1|1]);
    sum[id]=sum[id<<1]+sum[id<<1|1];
}
int qu1(int id,int l,int r,int k)
{
    if(l==r) return l;

    int ans=0;
    int mid=l+r>>1;
    if(sum[id<<1]>=k) return qu1(id<<1,l,mid,k);
    return qu1(id<<1|1,mid+1,r,k-sum[id<<1]);
}


void qu2(int id,int l,int r,int ql,int qr,int &ans1,int &ans2)
{

    if(ql<=l&&r<=qr){
        ans1=max(ans1,mx[id]);
        ans2=min(ans2,mi[id]);
        return ;
    }
    int mid=l+r>>1;
    if(ql<=mid) qu2(id<<1,l,mid,ql,qr,ans1,ans2);
    if(qr>mid) qu2(id<<1|1,mid+1,r,ql,qr,ans1,ans2);
}
int main()
{
    n=read(),m=read();
    build(1,1,n);
    while(m--)
    {
        int ty=read();
        if(ty==1){

            int k=read();
            int s=qu1(1,1,n,k);
            up(1,1,n,s);
        }
        else{
            int l=read(),r=read();
            l=qu1(1,1,n,l);
            r=qu1(1,1,n,r);

            int ans1=-inf,ans2=1e9;
            qu2(1,1,n,l,r,ans1,ans2);
            printf("%d %d\n",ans2,ans1);
        }
    }
}

D-瘦了的牛牛去旅游

2020年牛客算法入门课练习赛3 (A bfs B 容斥 C 线段树+主席树 D 暴力最短路 E 思维构造 )_第4张图片

数据n<=50

做法:由于n很小,考虑计算两个点内所有长度的距离。设dp[cnt][i][j] 为i点到j点  距离为cnt的最短路。

那么floyd 不仅要枚举中间节点k  还要枚举i到k的之间的距离l  进而得出k到j的中间距离 cnt-l

跑一边5层for循环即可。

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=52,inf=0x3f3f3f3f;

int dp[N][N][N];
int vis[N][N],n,m;
double ans[N][N];

int main()
{
	n=read();m=read();
	memset(dp,inf,sizeof(dp));
	rep(i,1,m){
        int u=read(),v=read(), w=read();
        vis[u][v]=1;
        dp[1][u][v]=min(dp[1][u][v],w);
	}

	for(int cnt=2;cnt<=n;++cnt){
        //printf("cnt:%d\n",cnt);
        for(int k=1;k<=n;++k){

            for(int i=1;i<=n;++i){
                if(!vis[i][k]) continue;
                for(int j=1;j<=n;++j){
                    
                    if(!vis[k][j]) continue;
                
                    vis[i][j]=1;
                    for(int l=0;l<=cnt;++l){
                        if(dp[l][i][k]>=inf||dp[cnt-l][k][j]>=inf) continue;
                        dp[cnt][i][j]=min(dp[cnt][i][j],dp[l][i][k]+dp[cnt-l][k][j]);
                    }
                    //puts("****");

                }


            }
        }
	}
	for (int i=1; i<=n; i++) {
        for (int j=1; j<=n; j++) {
            if (i==j) continue;
            if (!vis[i][j]) ans[i][j]=-1;
            else {
                double res=1e18;
                for (int k=1; k<=n; k++) {
                    res=min(res,dp[k][i][j]*1.0/k);
                }
                ans[i][j]=res;
            }
        }
    }



	int q=read();
	while(q--)
    {
        int u=read(),v=read();
        if (!vis[u][v]) printf("OMG!\n");
        else printf("%.3f\n",ans[u][v]);
    }
}

 

 

E-只能吃土豆的牛牛

2020年牛客算法入门课练习赛3 (A bfs B 容斥 C 线段树+主席树 D 暴力最短路 E 思维构造 )_第5张图片

做法:一定是前i个数进行 2^i -1 次方案数是前 (2^i)-1 小的。于是我们遍历找到第一个大于k的位置

答案加上 这个位置 的前面那个3^(i-1)  然后方案数k 减去2^(i-1) 继续 递归从前往后找 第一个大于k得位置即可。

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=33;
const ll MAX=1ll<<32;
ll f[N],ans,val[N];
void init()
{
    f[0]=1;
    val[0]=1;
    for(int i=1;ik){
            ans+=val[i-1];
            ans+=dfs(i,k-f[i-1]);
            break;
        }
    }
    return ans;
}
int main()
{
	init();
	int cas=0;
	int _=read();while(_--)
	{
	    ll k=read();
	    ans=dfs(33,k);
	    printf("Case #%d: %lld\n",++cas,ans);
	}
}

 

你可能感兴趣的:(牛客题解)