您的位置:首页 > 其它

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

2020-07-01 16:10 375 查看

钉钉、微博极速扩容黑科技,点击观看阿里云弹性计算年度发布会!>>>

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

A-胖胖的牛牛

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

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#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

;
int n;
ll vis

;
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_queue<node>que;
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-牛牛的零食

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

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

#pragma GCC optimize(2)
#include<bits/stdc++.h>

#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
,l,r,f
;
int n,len;

vector<pair<ll,ll> >num
;

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

ll ans=0;
for(int i=1;i<=len;++i){//m枚举状态
ll res=1;
int num1=0;
map<ll,ll>mp;
for(int j=0;j<n;++j){//枚举点
if(i&f[j]){
for(auto it:num[j]) mp[it.first]=max(mp[it.first],it.second);
num1++;
}
}

int flag=1;
for(auto it:mp){
res=res*powmod(it.first,it.second);
if(res>v) {
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<<n)-1;

ll ans = r / 8 - (l - 1) / 8 - ( run(r) - run(l-1) );

printf("%lld\n",ans);
}

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

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

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

#include<bits/stdc++.h>
#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<int, int>
#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-瘦了的牛牛去旅游

数据n<=50

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

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

跑一边5层for循环即可。

#include<bits/stdc++.h>
#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<int, int>
#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


;
int vis

,n,m;
double ans

;

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-只能吃土豆的牛牛

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

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

#include<bits/stdc++.h>
#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<int, int>
#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
,ans,val
;
void init()
{
f[0]=1;
val[0]=1;
for(int i=1;i<N;++i) {
f[i]=f[i-1]*2;
val[i]=val[i-1]*3;
}

}
ll dfs(int id,ll k)
{
ll ans=0;
if(id<=0||k<=0) return 0;
for(int i=0;i<id;++i){
if(f[i]>k){
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);
}
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐