校内模拟赛题解1
比赛一共6个题目,难度依次递增。
赛事排名第2,原来是霸榜第1两小时,后来每一个小学生反超了,@MichaelZeng。
T1
这个题目比较简单,赛时的时候没有太多,写了一个比较暴力的写法
这个题目的答案就 \(min(\frac{最长段}{2},次长段)\)
代码如下
#include<bits/stdc++.h>
using namespace std;
char s[100005];
int main(){
cin.tie(0)->sync_with_stdio(0);
freopen("fan.in","r",stdin);
freopen("fan.out","w",stdout);
cin>>s+1;
int n=strlen(s+1);
int g=0;
for(int i=1;i<=n;i++) g+=s[i]-'0';
if(g<=1) cout<<0<<endl;
else{
int cnt=0;
vector<int> ans;
for(int i=1;i<=n;i++){
if(s[i]=='0'){
ans.push_back(cnt);
cnt=0;
}else{
cnt++;
}
}
if(cnt!=0) ans.push_back(cnt);
sort(ans.begin(),ans.end(),greater<int>());
ans[0]/=2;
sort(ans.begin(),ans.end(),greater<int>());
cout<<ans[0]<<endl;
}
}
T2
题目传送门
这个题目赛时是第一个开的,想着抢一个一血,但是没抢到,而且第一题也被人抢了
这个题目就是贪心,我写了一个结构体,把相同的数堆在一起,然后不同的值按从大到小排序
统计答案的时候,如果当前这一堆数只有一个,那么只需要考虑他变成下一个数需要减去多少。如果不止一个,比如说有\(n\)个。那么根据题目,先把这\(n-1\)个数花费1全部归零,然后就剩一个了,那么接下来你就会了。记住,减完了之后要把这一堆数的数量加到下一堆数。
代码如下
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[500005];
struct info{
int cnt,v;
};
vector<info> s;
set<int> c;
bool cmp(info A,info B){
return A.v>B.v;
}
signed main(){
cin.tie(0)->sync_with_stdio(0);
freopen("ku.in","r",stdin);
freopen("ku.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
if(s.size()==0){
s.push_back({1,a[i]});
}else{
if(s.back().v==a[i]){
s.back().cnt++;
}else{
s.push_back({1,a[i]});
}
}
}
sort(s.begin(),s.end(),cmp);
int len=s.size(),ans=0;
s.push_back({0,0});
for(int i=0;i<len;i++){
auto [cnt,v]=s[i];
if(cnt==1){
ans+=v-s[i+1].v;
}else{
ans++;
ans+=v-s[i+1].v;
}
s[i+1].cnt+=cnt;
}
cout<<ans<<endl;
}
T3
这题目还是蛮难的,一般想不到。赛时这个题目我是一血
首先,这个题目可以转化成一个图论题。
也就是每一个书都对应一个点
对于一个\(j\),它可以通过某个\(i\),变成\(j-a_i+b_i\),满足\(j≥a_i\)。
那么这个题就是看对于点n可达哪些点,求这些点中编号最小的
代码如下
#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<int> edge[5005];
bool b[5005];
int main(){
cin.tie(0)->sync_with_stdio(0);
freopen("fan.in","r",stdin);
freopen("fan.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b;cin>>a>>b;
for(int j=a;j<=n;j++)
edge[j].push_back(j-a+b);
}
queue<int> q;
q.push(n);
b[n]=1;
while(q.size()){
int x=q.front();
q.pop();
for(auto y:edge[x]){
if(!b[y]){
b[y]=1;q.push(y);
}
}
}
for(int i=0;i<=n;i++){
if(b[i]){
cout<<i<<endl;
return 0;
}
}
}
T4
题目传送门
这个题目我觉得还挺简单的,但是官方题解写的挺复杂的,有兴趣的话可以看一下。
这个题目依旧图论,然后加了一个简直就过了。赛时的时候也没有管多少,交上去就过了。但是后来仔细一想,时间复杂度可能是\(O(n^6)\),但是好像又不是这样的,然后我问了一下豆包,他说是\(O(n^6)\),但其实平均是\(O(n^4)\)。
后来我有写了一个代码,标准的\(O(n^4)\),至少豆包是这么认为的。但是交上去更慢了,也不知道是不是测评机波动。
首先建边,按照题以即可。
然后就是多源bfs,循环遍历每一个点去做一次bfs,但是不是所有的点都要加进去的,这个东西我们后面会解释。
bfs的时候,首先有一个全局数组b来表示这个点在所有已经完成的bfs中有没有走过。然后对于每一次bfs,新建一个vis数组表示这轮bfs有没有走过。这也就意味着有可能一个点之前走过,但是在后面的一轮bfs还是走过了。这个其实不会过多影响bfs。如果你一味的优化,直接判断这个点有没有在之前的bfs走过的话,可能对于答案的统计就会有重大的影响。
然后bfs完了之后,我们需要更新b数组,对于任意一个\(b_{i,j}\),都有\(b_{i,j}=b_{i,j}|a_{i,j}\)
接下来退出bfs,在循环遍历每个点时,要去看\(b_{i,j}\)是不是true,即在之前每轮的bfs中,有没有走过。
因为如果走过了,再更新它也没有意义了,因为这个点已经被一个点激活过了,那么在那一轮bfs中这个点就会被bfs,所以答案只会减少,不会增加。
综上,代码如下
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define all(x) x.begin(),x.end()
#define sz(x) (int)(x.size())
#define ret return
using ll=long long;
using vt=vector<int>;
using vl=vector<ll>;
using pii=pair<int,int>;
const int N=105;
const int mod=998244353;
const int P=1e9+7;
void pt(string s){cout<<s<<endl;}
void gmin(int &x,int y){x=max(x,y);}
void gmax(int &x,int y){x=max(x,y);}
vector<pii> edge[N][N];
int n,a[N][N],ans=INT_MIN;
bool b[N][N];
//add edge x->y;
void add(int xa,int ya,int xb,int yb){edge[xa][ya].push_back({xb,yb});}
void bfs(int i,int j){
vector<vector<bool>> vis(n+1,vector<bool>(n+1,0));
queue<pair<int,int>> q;
vis[i][j]=1;
q.push({i,j});
int cnt=1;
while(q.size()){
auto [x,y]=q.front();
q.pop();
for(auto [xx,yy]:edge[x][y])
if(!vis[xx][yy])
cnt++,vis[xx][yy]=1,
q.push({xx,yy});
}
ans=max(ans,cnt);
for(int k=1;k<=n;k++)
for(int l=1;l<=n;l++)
b[k][l]|=vis[k][l];
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
for(int l=1;l<=n;l++)
if(max(abs(i-k),abs(j-l))==a[i][j]&&!(i==k&&j==l))
add(i,j,k,l);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!b[i][j])
bfs(i,j);
cout<<ans<<endl;
}
int main(){
cin.tie(0)->sync_with_stdio(0);
freopen("lian.in","r",stdin);
freopen("lian.out","w",stdout);
int t=1;
//cin>>t;
while(t--) solve();
}
是的,你发现码风突变,因为这是赛后补写的!
T5
这个题目放在T5实在太简单了,赛时又是一血。
其实就是一个连通块计数,然后对于每一个连通块可以去统计面积和守城人
最后统计答案的时候不是要排序吗(其实找最大值就可以了),比较分数的时候建议存储分子和分母(其实就是守城人和面积),而不是存储小数,避免精度误差(虽然对于这个题目不会有精度问题)
比较两个分数\(\frac{x_1}{y_1}\)和\(\frac{x_2}{y_2}\)的大小,等价于比较\(\frac{x_1y_2}{y_1}\)和\(\frac{x_2y_1}{y_2}\),继续化简即可得到\(x_1y_2\)和\(x_2y_1\)
代码如下
#include<bits/stdc++.h>
using namespace std;
int n,d[8][2]={
{-1,0},{1,0},{0,-1},{0,1},
{-1,-1},{-1,1},{1,-1},{1,1}
};
char s[505][505];
bool b[505][505];
struct info{
int v,m;
};
vector<info> ans;
bool cmp(info A,info B){
if(A.v*B.m==B.v*A.m) return A.v<B.v;
return A.v*B.m<B.v*A.m;
}
void bfs(int i,int j){
int cnt1=0,cnt2=0;
b[i][j]=1,cnt1++;
if(s[i][j]=='@') cnt2++;
queue<pair<int,int>> q;
q.push({i,j});
while(q.size()){
auto [x,y]=q.front();
q.pop();
for(int i=0;i<8;i++){
int xx=x+d[i][0],yy=y+d[i][1];
if(xx<1||yy<1||xx>n||yy>n) continue;
if(s[xx][yy]=='#') continue;
if(b[xx][yy]) continue;
cnt1++;
if(s[xx][yy]=='@') cnt2++;
q.push({xx,yy});b[xx][yy]=1;
}
}
ans.push_back({cnt2,cnt1});
}
int main(){
cin.tie(0)->sync_with_stdio(0);
freopen("kong.in","r",stdin);
freopen("kong.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++) cin>>s[i]+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(s[i][j]!='#'){
if(!b[i][j]){
bfs(i,j);
}
}
}
}
sort(ans.begin(),ans.end(),cmp);
cout<<ans.size()<<' '<<ans[0].v<<endl;
}
终于……我们来到了最后一题——交互题~
T6
观察题目,20次询问覆盖1e5,基本上就是二分了。
首先,我们要确定整个数组次大值。这样有便于我们确定最大值
这个时候,我们要分三种情况
1.得到的次大值位置为1
对于这种情况,我们一下就知道最大值在2~n-1。然后我们假设最大值的位置在x,那么对于以下这些询问,返回的位置不是次大值的位置:
1 2,1 3,1 4,......,1 x-1
对于以下这些询问,返回的是次大值的位置:
1 x,1 x+1,1 x+2,......,1 n;
那么,如果一个询问返回的是次大值的位置说明什么,说明这个区间存在最大值。所以,这个题目就可以二分。那么对于这种情况,每次询问一个1 m,如果返回的是次大值的位置,那么r=m,否则l=m
答案为r
2.得到的次大值的位置为n
可以确定区间1~n-1,然后每次询问时m n,如果返回的是次大值的位置,l=m,否则r=m。
答案为l
3.得到次大值的位置在2~n-1之间
那么还需要确定最大值的区间,可以询问1 次大值位置,如果返回的仍然是次大值,那么跟情况1一样处理。
如果不是,那么就跟情况2一样处理
综上,这个题目就解决了
代码如下
#include<bits/stdc++.h>
using namespace std;
int n,bk,bk2;
int main(){
cin.tie(0)->sync_with_stdio(0);
cin>>n;
cout<<"? 1 "<<n<<endl;
cin>>bk;
if(bk==1){
int l=1,r=n+1;
while(l+1<r){
int m=l+r>>1;
cout<<"? 1 "<<m<<endl;
cin>>bk2;
if(bk2==bk) r=m;
else l=m;
}
cout<<"! "<<r<<endl;
}else if(bk==n){
int l=0,r=n;
while(l+1<r){
int m=l+r>>1;
cout<<"? "<<m<<' '<<n<<endl;
cin>>bk2;
if(bk2==bk) l=m;
else r=m;
}
cout<<"! "<<l<<endl;
}else{
cout<<"? 1 "<<bk<<endl;
cin>>bk2;
if(bk2==bk){
int l=0,r=bk2;
while(l+1<r){
int m=l+r>>1;
cout<<"? "<<m<<' '<<bk2<<endl;
cin>>bk;
if(bk==bk2) l=m;
else r=m;
}
cout<<"! "<<l<<endl;
}else{
int l=bk,r=n+1;
while(l+1<r){
int m=l+r>>1;
cout<<"? "<<bk<<' '<<m<<endl;
cin>>bk2;
if(bk2==bk) r=m;
else l=m;
}
cout<<"! "<<r<<endl;
}
}
}
oh,终于结束了!感谢观看!

浙公网安备 33010602011771号