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的坐标,对于这一过程我们显然需要维护容量的前缀和然后二分。我们希望价值最大
标程做法:首先枚举左端点,然后根据 \(W\) 的限制求解出右端点的最小值(双指针递推),记为 \(r\)。
那么此时问题转变为:以 \(r\) 为左端点的最大子段和(通过倒序 dp 预处理即可)。
时间复杂度:\(O(n)\)
标签:连通,int,long,牛客,2000,小白月赛,做法,86,define From: https://www.cnblogs.com/mathiter/p/17994274