牛客小白月赛22比赛界面
小白月赛22题解
难度体验
签到:E.模拟、F.思维
简单:D.爆搜、G.暴力、J.大数模板
中级:A.STL、B.树形DP
困难:C.记忆化搜索、H.差分
压轴:I.计算几何
A.操作序列
题意
给出一个长度无限的数列,初始全部为零,有三种操作:
- 增加操作:给下标为 $t$ 的数加 $c$。特别注意,如果在下标$[t-30,t+30]$
内有不为零的数,增加操作无效。
- 削减操作:让数列中下标最小的不为零数变为零。
- 查询操作:查询数列中下标为$t$的数字是多少。
思路
STL模拟,用map维护实际数组,set维护非0数组的下标。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007; map<int,int>mp; set<int>st; map<int,int>::iterator it; set<int>::iterator it2; void add(int x,int c) { bool flag=1; it2=st.lower_bound(x-30); for(set<int>::iterator it3=it2;it3!=st.end();it3++) { if(*it3<=x+30) return; else break; } if(mp[x]+c==0) { mp.erase(x); st.erase(x); } st.insert(x); mp[x]+=c; } int sub() { if(st.empty()) return 0; it=mp.begin(); int ret=it->second; st.erase(st.begin()); mp.erase(mp.begin()); return ret; } int query(int x) { it2=st.find(x); if(it2==st.end()) return 0; return mp[x]; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); #ifdef DEBUG freopen("input.in", "r", stdin); #endif int n,t,c; scanf("%d",&n); while(n--) { char ch='?'; scanf("%d%c",&t,&ch); if(ch==' ') { scanf("%d",&c); add(t,c); continue; } if(t==-1) { if(st.empty()) printf("skipped\n"); else{ printf("%d\n",sub()); } } else printf("%d\n",query(t)); } return 0; }
|
B.树上子链
题意
给定一棵树 T ,树 T 上每个点都有一个权值。
定义一颗树的子链的大小为:这个子链上所有结点的权值和 。
请在树 T 中找出一条最大的子链并输出。
思路
前缀和,树形DP。
通过DFS维护根节点到节点x的前缀和,以及x节点到叶子结点的最大值与次大值。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007; vector<int>G[maxn]; ll val[maxn],sum[maxn],dp[maxn],dp2[maxn],ans; void dfs(int x,int fa) { sum[x]=sum[fa]+val[x]; dp[x]=val[x]; dp2[x]=-inf; for(auto &v:G[x]) { if(v==fa) continue; dfs(v,x); if(dp[v]+val[x]>dp[x]) { dp2[x]=dp[x]; dp[x]=dp[v]+val[x]; } else dp2[x]=max(dp2[x],dp[v]+val[x]); } ans=max(ans,max(sum[fa]+dp[x],dp[x]+dp2[x]-val[x])); } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); #ifdef DEBUG freopen("input.in", "r", stdin); #endif int n,u,v; cin>>n; for(int i=1;i<=n;i++) cin>>val[i]; for(int i=1;i<n;i++) { cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } ans=val[1]; dfs(1,0); cout<<ans<<endl; return 0; }
|
C.交换游戏
题意
有12个孔,有些孔上面有障碍。
若如果相邻的三个孔有两个孔被遮挡,并且被遮挡的两个孔相邻,则可以将中间的障碍拿掉,并将一端的遮挡物移到另一端没有被遮挡的孔上面。
简单来说:将这三个孔的状态反转。
例如记x为障碍遮住的孔,o为未遮住的孔
oxx -> xoo,xxo -> oox
思路
记忆化搜索
将状态看作二进制,枚举计算每一种组合的答案,$O(1)$查询。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007; bool vis[1<<13]; int ans[1<<13]; void dfs(int x) { if(vis[x]) return; vis[x]=1; int cnt=0; for(int i=1;i<=(1<<11);i<<=1) if(x&i) cnt++; ans[x]=cnt; for(int i=3,t1=1,t2=2,t3=4;i<=12;i++,t1<<=1,t2<<=1,t3<<=1) { if((x&t2)&&!((x&t1)&&(x&t3))&&((x&t1)||(x&t3))) { if(x&t1) { dfs(x-t2-t1+t3); ans[x]=min(ans[x],ans[x-t2-t1+t3]); } else{ dfs(x-t2-t3+t1); ans[x]=min(ans[x],ans[x-t2-t3+t1]); } } } } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); #ifdef DEBUG freopen("input.in", "r", stdin); #endif for(int i=0;i<(1<<12);i++) dfs(i); int t; string s; cin>>t; while(t--) { cin>>s; int now=0; for(auto &ch:s) { now<<=1; if(ch=='o') now|=1; } cout<<ans[now]<<endl; } return 0; }
|
D.收集纸片
题意
我们把房间按照笛卡尔坐标系进行建模之后,每个点就有了一个坐标。
假设现在房子里有些纸片需要被收集,收集完纸片你还要回归到原来的位置,你需要制定一个策略来使得自己行走的距离最短。
你只能沿着 x 轴或 y 轴方向移动:从位置 (i,j) 移动到相邻位置 (i+1,j),(i-1,j),(i,j+1) 或 (i,j-1) 距离增加 1。
思路
范围不大,爆搜即可
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007; struct node { int x,y; } a[15]; int n,sx,sy,ans; int dis(int x1,int y1,int x2,int y2) { return abs(x1-x2)+abs(y1-y2); } int dis(int s,int t) { if(s==0) return abs(sx-a[t].x)+abs(sy-a[t].y); return abs(a[s].x-a[t].x)+abs(a[s].y-a[t].y); } bool vis[maxn];
void dfs(int t,int tim,int tot) { if(tim==n) { ans=min(ans,tot+dis(sx,sy,a[t].x,a[t].y)); return; } for(int i=1;i<=n;i++) { if(!vis[i]) { vis[i]=1; dfs(i,tim+1,tot+dis(t,i)); vis[i]=0; } } } int main() { int t,r,c; cin>>t; while(t--) { cin>>r>>c>>sx>>sy>>n; for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y; ans=inf; dfs(0,0,0); cout<<"The shortest path has length "<<ans<<endl; } return 0; }
|
E. 方块涂色
签到题
1 2 3 4 5 6 7 8 9 10 11 12
| #include<bits/stdc++.h> using namespace std; typedef long long ll; int main() { ll n,m,r,c; while(cin>>n>>m>>r>>c) { cout<<(n-r)*(m-c)<<endl; } return 0; }
|
F. 累乘数字
签到题,有点小思维
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007; int main() { string n; ll d; while(cin>>n>>d) { cout<<n; while(d--) cout<<"00"; cout<<endl; } return 0; }
|
G.仓库选址
思路
比赛时没开这题……$O(n^4)$无脑暴力做
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=105,inf=0x3f3f3f3f,mod=1000000007; int mp[maxn][maxn]; int dis(int x1,int y1,int x2,int y2) { return abs(x1-x2)+abs(y1-y2); } int main() { int t,n,m; cin>>t; while(t--) { cin>>m>>n; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>mp[i][j]; int ans=inf; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int now=0; for(int k=1;k<=n;k++) for(int l=1;l<=m;l++) now+=mp[k][l]*dis(i,j,k,l); ans=min(ans,now); } cout<<ans<<endl; } return 0; }
|
H.货物种类
比赛时看了题目想了树套树的假做法,觉得空间复杂度炸裂+难写就没继续想……
题意
n个仓库,编号从1到n。
当购进某种货物的时候,商家会把货物分散的放在编号相邻的几个仓库中。
当所有货物购买完毕,存放货物种类最多的仓库编号为多少?
思路
离散化,差分,看代码
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007; struct ope { int l,r,g; ope(int l,int r,int g): l(l),r(r),g(g){} }; vector<int>des,be[maxn],ed[maxn]; int res[maxn]; int main() { vector<ope>op; int n,m,l,r,d,cnt=0; cin>>n>>m; while(m--) { cin>>l>>r>>d; op.push_back(ope(l,r,d)); des.push_back(d); } sort(des.begin(),des.end()); des.erase(unique(des.begin(),des.end()),des.end()); for(auto &i:op) { i.g=lower_bound(des.begin(),des.end(),i.g)-des.begin()+1; be[i.l].push_back(i.g); ed[i.r].push_back(i.g); } int ans=0,num=0,now=0; for(int i=1;i<=n;i++) { for(int j:be[i]) { if(!res[j]) now++; res[j]++; } if(now>ans) { ans=now; num=i; } for(int j:ed[i]) { res[j]--; if(!res[j]) now--; } } cout<<num<<endl; return 0; }
|
I.工具人
思路
一句话题解+玄学代码看了我一晚上……
计算出每个点可以被射中的角度范围,放到循环数组里按角度排序。
枚举所有点作为起点,贪心地求出最小开枪数。
还是不很理解
下面这个代码大概就是照着题解敲了一遍并加上自己的理解,并且在核心代码有这么一行
如果换成
同样可以过,而且更好理解。
比较疑惑为什么按题解的写法可以保证最后数组中元素数量不大于1,如果有大佬会的话请务必在下面留言教教本菜鸡QAQ
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007; const double eps= 1e-8,pi=acos(-1);
void read(){} template<typename T,typename... T2>inline void read(T &x,T2 &... oth) { x=0; int ch=getchar(),f=0; while(ch<'0'||ch>'9'){if (ch=='-') f=1;ch=getchar();} while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} if(f)x=-x; read(oth...); } struct node { int type,id; double angle; node (int a,double b,int c): type(a),id(c),angle(fmod(b+2*pi,2*pi)){} bool operator <(const node &b) { if(abs(angle-b.angle)>eps) return angle<b.angle; else return type<b.type; } }; int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); #ifdef DEBUG freopen("input.in", "r", stdin); #endif int t,n,d,c; cin>>t; while(t--) { cin>>n>>d; vector<node> v; for(int i=c=0;i<n;i++) { double x,y; cin>>x>>y; if(x*x+y*y<=d*d+eps) continue; double a=atan2(y,x); double t=asin(d/sqrt(x*x+y*y)); v.push_back(node(0,a-t,c)); v.push_back(node(1,a+t,c++)); } if(c==0) { cout<<1<<endl; continue; } sort(v.begin(),v.end()); int ans=c; for(int i=0;i<v.size();i++) { int cnt=0; vector<bool>vis(c,0); vector<int>rec; for(int k=0;k<v.size();k++) { int j=(i+k)%v.size(); if(v[j].type==0) { vis[v[j].id]=1; rec.push_back(v[j].id); } else if(vis[v[j].id]) { cnt++; for(int l:rec) vis[l]=0; rec.clear(); } } cnt+=rec.size(); ans=min(ans,cnt); } cout<<ans<<endl; } return 0; }
|
J.计算A+B
题目没啥好说的,一个裸的大数板子
思路
大数加法
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1000000007;
void read(){} template<typename T,typename... T2>inline void read(T &x,T2 &... oth) { x=0; int ch=getchar(),f=0; while(ch<'0'||ch>'9'){if (ch=='-') f=1;ch=getchar();} while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} if(f)x=-x; read(oth...); } const int Length=10005; string add(const string &a,const string &b) { string ans; int na[Length]={0},nb[Length]={0}; int la=a.size(),lb=b.size(); for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0'; for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0'; int lmax=la>lb?la:lb; for(int i=0;i<lmax;i++) na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10; if(na[lmax]) lmax++; for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0'; return ans; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); #ifdef DEBUG freopen("input.in", "r", stdin); #endif int n; string s; cin>>n; while(n--) { cin>>s; string a,b; bool flag=0; for(auto &ch:s) { if(ch=='+') { flag=1; continue; } if(!flag) a+=ch; else b+=ch; } if(a.empty()||b.empty()) cout<<"skipped"<<endl; else{ cout<<add(a,b)<<endl; } } return 0; }
|