模拟赛爆炸祭。
T1
把所有连通块依次求出,若某个连通块的数量已经出现过,则说明它与以前的连通块属于同一星系,直接将星系大小加上连通块大小并取 \(\max\);否则将星系数量 \(+1\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int ans=-1e9,num,sum;
int g[100031];
int dx[]={-1,1,0,0,-1,-1,1,1},dy[]={0,0,-1,1,-1,1,-1,1};
bool vis[1531][1531],v[1531];
char a[1531][1531];
void dfs(int x,int y){
num++,vis[x][y]=1;
for(int i=0;i<8;i++){
int xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&!vis[xx][yy]&&a[xx][yy]=='*')
dfs(xx,yy);
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]=='*'&&!vis[i][j]){
num=0;
dfs(i,j);
if(!v[num]) sum++,v[num]=1;
g[num]+=num;
}
}
}
for(int i=1;i<=100000;i++) ans=max(ans,g[i]);
cout<<sum<<' '<<ans;
return 0;
}
T2
一眼二分。在 check
函数中遍历整个数列,若没超过最大和就不断累加数列元素,否则将段数 \(+1\),最后判断是否能分成 \(\le m\) 段即可。
注意段数最少为 \(1\),且左边界最小为 \(\max\{a_i\}\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a[100031],maxi=-1e9;
bool check(int x){
int sum=0,cnt=1;
for(int i=1;i<=n;i++){
if(sum+a[i]<=x) sum+=a[i];
else cnt++,sum=a[i];
}
return cnt<=m;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],maxi=max(maxi,a[i]);
int l=maxi-1,r=1e9+1;
while(l+1<r){
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid;
}
cout<<r;
return 0;
}
T3
因为天数具有单调性,所以仍考虑二分需要修改的那一天 \(mid\)。
在 check
函数中循环 \(1 \sim mid\) 天,不断使用差分数组进行区间减操作,最后求出前缀和数组,若其中某个元素 \(<0\) 则返回 true
,否则返回 false
。注意先将前缀和数组清空。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int a[1000031],d[1000031],s[1000031],t[1000031];
int diff[1000031],sum[1000031];
bool check(int x){
//cout<<s[x]<<'\n';
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++) diff[i]=a[i]-a[i-1];
for(int i=1;i<=x;i++)
diff[s[i]]-=d[i],diff[t[i]+1]+=d[i];
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+diff[i];
if(sum[i]<0) return true;
}
return false;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i],diff[i]=a[i]-a[i-1];
for(int i=1;i<=m;i++)
cin>>d[i]>>s[i]>>t[i];
int l=0,r=n+1;
//cout<<check(2)<<' '<<check(1)<<'\n';
while(l+1<r){
int mid=(l+r)>>1;
//cout<<mid<<'\n';
if(check(mid)) r=mid;
else l=mid;
}
if(r==n+1) cout<<0;
else cout<<-1<<'\n'<<r;
return 0;
}
T4
原题,略过。