牛客小白月赛86

B 水平考试

======

等价于两个集合 S,T 判断 S 中是否存在 T 中所不包含的元素。

  • 若存在则输出 0;
  • 否则输出 10。

时间复杂度:O(T)

C题:区间查询当前区间可以被分为多少段,要求每段只有一种数字。

做法1:提前对所有段编号,查询时直接左右边界编号相减,注意边界需要特别处理
做法2:标记第i段与第i+1段之间的分界点,然后求前缀和,本质上和做法1一样

D题:给定网格,0表示障碍,1表示道路。问有多少块长方形道路?(正方形也是长方形)

对此我们首先用过bfs找到连通块,对于每个连通块我们需要维护这个连通块最大x,y坐标,
最小x,y坐标,以及连通块大小。当连通块是矩形的时候通过
统计矩形的(minX, minY) -> (maxX, maxY)

然后判断 (maxY - minY + 1) * (maxX - minX + 1) == 连通图的大小

// Problem: 剪纸游戏 // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/73450/D // Memory Limit: 524288 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; #define ll long long # define int long long #define ull unsigned long long #define pii pair<int,int> #define baoliu(x, y) cout << fixed << setprecision(y) << x #define endl "\n" #define debug1(x) cerr<<x<<" " #define debug2(x) cerr<<x<<endl const int N = 1e3 + 10; const int M = 1e6 + 10; const int inf = 0x3f3f3f3f; const int mod = 998244353; const double eps = 1e-8; int n, m; bool st[N][N]; int a[N][N]; int dx[5]={0,1,-1,0}; int dy[5]={1,0,0,-1}; vector<pii>mx(N*N,{-1,-1}); vector<pii>mn(N*N,{2000,2000}); vector<int>sz(N*N,0); queue<pii>q; int cnt=0; void bfs(int x,int y){ //cerr<<x<<" "<<y<<endl; q.push({x,y}); st[x][y]=true; sz[cnt]++; mx[cnt].first=max(mx[cnt].first,x); mx[cnt].second=max(mx[cnt].second,y); // mx[cnt]=max(mx[cnt],{x,y}); // mn[cnt]=min(mn[cnt],{x,y}); mn[cnt].first=min(mn[cnt].first,x); mn[cnt].second=min(mn[cnt].second,y); while(q.size()){ auto t=q.front();q.pop(); for(int i=0;i<4;i++){ int tx=t.first+dx[i]; int ty=t.second+dy[i]; if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&a[tx][ty]&&!st[tx][ty]){ q.push({tx,ty});st[tx][ty]=true; sz[cnt]++; mx[cnt].first=max(mx[cnt].first,tx); mx[cnt].second=max(mx[cnt].second,ty); mn[cnt].first=min(mn[cnt].first,tx); mn[cnt].second=min(mn[cnt].second,ty); //mx[cnt]=max(mx[cnt],{tx,ty}); //mn[cnt]=min(mn[cnt],{tx,ty}); } } } // cerr<<sz[cnt]<<endl; // cerr<<mx[cnt].first<<" "<<mx[cnt].second<<endl; // cerr<<mn[cnt].first<<" "<<mn[cnt].second<<endl; // cerr<<"***************************"<<endl; } void solve(){ cin>>n>>m; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ char c;cin>>c; if(c=='.')a[i][j]=1; else a[i][j]=0; //cerr<<a[i][j]<<" "; } //cerr<<endl; } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(a[i][j]==0||st[i][j])continue; bfs(i,j); cnt++; } } int res=0; //cerr<<cnt<<endl; for(int i=0;i<cnt;i++){ int l=mx[i].first-mn[i].first+1; int r=mx[i].second-mn[i].second+1; if(l*r==sz[i]){ res++; //cerr<<l<<" "<<r<<' '<<sz[cnt]<<endl; } } cout<<res<<endl; } signed main() { cin.tie(0); ios::sync_with_stdio(false); int t; //cin>>t; t=1; while (t--) { solve(); } return 0; }

E题:带容量下限的最大连续子数组和

简单无脑做法:

  • 枚举每个起点,他的终点是j到n的任意一点。j是从左到右第一个满足当前区间容量大于W的坐标,对于这一过程我们显然需要维护容量的前缀和然后二分。我们希望价值最大,在终点到n中,我们需要找到最大的前缀和,因为起点被我们枚举从而固定,对此我们提前预处理后缀最大前缀和。
// Problem: 可口蛋糕 // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/73450/E // Memory Limit: 524288 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; #define ll long long # define int long long #define ull unsigned long long #define pii pair<int,int> #define baoliu(x, y) cout << fixed << setprecision(y) << x #define endl "\n" #define debug1(x) cerr<<x<<" " #define debug2(x) cerr<<x<<endl const int N = 1e6+5; const int M = 1e6 + 10; const int inf = 0x3f3f3f3f; const int mod = 998244353; const double eps = 1e-8; int n, m; int a[N]; int w[N],v[N]; int sw[N],sv[N]; int mx[N]; //v是体积,w是价值 void solve(){ cin>>n>>m; for(int i=1;i<=n;i++)cin>>v[i]; for(int i=1;i<=n;i++)sv[i]=sv[i-1]+v[i]; //for(int i=1;i<=n;i++)cerr<<sv[i]<<" "; //cerr<<endl; for(int i=1;i<=n;i++)cin>>w[i]; for(int i=1;i<=n;i++)sw[i]=sw[i-1]+w[i]; //for(int i=1;i<=n;i++)cerr<<sw[i]<<" "; //cerr<<endl; for(int i=n;i>=1;i--){ if(i==n)mx[i]=sw[i]; else mx[i]=max(mx[i+1],sw[i]); } //for(int i=1;i<=n;i++)cerr<<mx[i]<<" "; //cerr<<endl; int ans=-1e18; for(int i=1;i<=n;i++){ int l=i; int val=sv[i-1]+m; int r=lower_bound(sv+1,sv+n+1,val)-sv; if(sv[r]-sv[l-1]<m)continue; // cerr<<l<<" "<<r<<endl; int res=mx[r]-sw[l-1]; //cerr<<l<<" "<<r<<" "<<res<<endl; ans=max(ans,res); } cout<<ans<<endl; } signed main() { cin.tie(0); ios::sync_with_stdio(false); int t; //cin>>t; t=1; while (t--) { solve(); } return 0; }

标程做法:首先枚举左端点,然后根据 W 的限制求解出右端点的最小值(双指针递推),记为 r

那么此时问题转变为:以 r 为左端点的最大子段和(通过倒序 dp 预处理即可)。

时间复杂度:O(n)


__EOF__

本文作者爱飞鱼
本文链接https://www.cnblogs.com/mathiter/p/17994274.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   potential-star  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示