BUU-ACM选拔赛第一场题解
A题 排行榜(模拟)
难度:easy
这题根据题意模拟即可,部分同学WA的原因有几个方面:
1.题意理解不清,注意各个比较条件
2.字符串读入有问题
选用scanf读入字符串时用scanf(“%s”,s1),判断两队直接比赛状态时直接比较首字母即可,或者调用字符串比较函数
选用string读入字符串时用cin即可,比较字符串直接选用==即可
#include<bits/stdc++.h>
using namespace std;
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
int t;
cin>>t;
while(t--)
{
string name1;
int w1,d1,l1;
int jin1,shi1,pen1;
cin>>name1>>w1>>d1>>l1>>jin1>>shi1>>pen1;
string name2;
int w2,d2,l2;
int jin2,shi2,pen2;
cin>>name2>>w2>>d2>>l2>>jin2>>shi2>>pen2;
int score1 = w1 * 3 + d1;
int score2 = w2 * 3 + d2;
string res;
cin>>res;
if(score1>score2)
{
cout<<name1<<endl;
continue;
}
else if(score1<score2)
{
cout<<name2<<endl;
continue;
}
if(res=="win")
{
cout<<name1<<endl;
continue;
}
else if(res=="lose")
{
cout<<name2<<endl;
continue;
}
int qiu1 = jin1 - shi1;
int qiu2 = jin2 - shi2;
if(qiu1>qiu2)
{
cout<<name1<<endl;
continue;
}
else if(qiu1<qiu2)
{
cout<<name2<<endl;
continue;
}
if(jin1>jin2)
{
cout<<name1<<endl;
continue;
}
else if(jin1<jin2)
{
cout<<name2<<endl;
continue;
}
if(w1>w2)
{
cout<<name1<<endl;
continue;
}
else if(w1<w2)
{
cout<<name2<<endl;
continue;
}
if(pen1<pen2)
{
cout<<name1<<endl;
continue;
}
else if(pen1>pen2)
{
cout<<name2<<endl;
continue;
}
else
cout<<"good luck"<<endl;
}
return 0;
}
B题 嘀嘀嘀(模拟、格式化读入)
难度:easy
本题错误点主要在于大家对数据范围的忽视,题目给出的k的范围为小于1e9,因此小w可能有睡好几天甚至好几年的可能性,因此对于秒数我们需要先将其对86400取模,然后再进行小时、分钟、秒的处理,注意输出时不需要很麻烦,直接选用printf格式化输出即可自动处理前导0的问题。
#include<bits/stdc++.h>
using namespace std;
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
int t;
cin>>t;
while(t--)
{
int t1,t2,t3;
scanf("%d:%d:%d",&t1,&t2,&t3);
int k;
cin>>k;
int kk = k % (24*60*60);
int st = t1 * 60 * 60 + t2 * 60 + t3;
int ed = (st + kk) % (24*60*60);
// cout<<st<<" "<<ed<<endl;
int ans1,ans2,ans3;
ans1 = ed / 3600;
ans2 = (ed - (ans1 * 3600) ) / 60;
ans3 = ed - ans1 * 3600 - ans2 * 60;
printf("%02d:%02d:%02d\n",ans1,ans2,ans3);
// cout<<ans1<<":"<<ans2<<":"<<ans3<<endl;
}
return 0;
}
C题 搭积木第三季(DP or 暴力)
难度:mid-
本题计算暴力时间复杂度为3e8,而题目给出3s的时限,因此本题放开了大家暴力进行尝试,但是很可惜大家都没有尝试暴力解决本题
#include<iostream>
using namespace std;
int n,cnt = 0;
int a[10];
void dfs(int deep){
if(deep == n + 1) {
cnt++;
return ;
}
for(int i=0;a[deep]>=i;i++){
if(a[deep]-i>=a[deep-1]){
a[deep] -=i;
dfs(deep + 1);
a[deep] += i;
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
dfs(1);
cout<<cnt<<endl;
return 0;
}
D题 余数问题(打表找规律)
难度:mid
首先观察数据范围为1e12,首先暴力妥妥的tle,那么再看本题的特点,我们可以先看看小数据范围时每个数的答案,将所有答案打出后,规律就显而易见了
#include<bits/stdc++.h>
using namespace std;
long long mul(long long x,long long y)
{
long long ans = 1;
while(y)
{
if(y&1)
{
ans = x * ans;
}
x=x*x;
y=y>>1;
}
return ans ;
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
int t;
cin>>t;
while(t--)
{
long long n;
scanf("%lld",&n);
long long x = n;
int wei = 0;
while(x)
{
x=x/2;
wei++;
}
if(n == 1)
{
cout<<"0"<<endl;
// printf("0\n");
continue;
}
if(n == (mul(2,wei-1)))
{
cout<<n/2-1<<endl;
// printf("%lld\n",n/2-1);
}
else
{
// cout<<mul(2,wei)/2-1<<endl;
long long ans = mul(2,wei)/2-1;
printf("%lld\n",ans);
}
}
return 0;
}
E题 病毒侵袭 (prim or Kruskal最小生成树)
难度:mid
本题题意很简单,主要是如何转化为最小生成树问题,首先我们要将所有点全部联通,联通性的判断我们可以考虑用并查集,然后就是考虑选边,由于题目要求是最小的,因此我们将所有边按照边权大小从小到大排序,然后依次选边。当所有点相互联通时,那条边即为我们所求的答案。这一过程其实就是kruskal选边的过程,因此直接套用最小生成树模板解决即可
#include<bits/stdc++.h>
using namespace std;
const int N = 5005;
long long xx[N];
long long yy[N];
int f[N];
int n;
int getf(int x)
{
if(f[x]==x)
return x;
else
return f[x]=getf(f[x]);
}
struct edge
{
int u,v;
long long d;
bool operator <(const edge &x)const
{
return d<x.d;
}
}e[N*N];
long long ans = 0;
int tot = 0;
//bool cmp(edge a,edge b)
//{
// return a.d<b.d;
//};
void kruskal()
{
sort(e+1,e+tot+1);
int cnt = 0;
ans = 0;
for(int i=1;i<=tot;i++)
{
int eu = getf(e[i].u);
int ev = getf(e[i].v);
if(eu == ev)
continue;
f[eu] = ev;
cnt++;
ans = max(ans,e[i].d);
if(cnt == n-1)
break;
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&xx[i],&yy[i]);
f[i] = i;
}
tot = 0;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
e[++tot].u=i;
e[tot].v=j;
e[tot].d=((xx[i]-xx[j])*(xx[i]-xx[j])+(yy[i]-yy[j])*(yy[i]-yy[j]));
// printf("%lld\n",e[tot].d);
}
}
kruskal();
printf("%lld\n",ans);
}
return 0;
}
F题 勇者闯关 (线段树)
难度:hard
首先我们先将所有障碍物进行按照x轴和y轴进行排序,然后我们对每一行建立一个线段树。首先对于任何一个点,如果他左面和上面都是不可到达的,那么这个点就是不可到达的,反之即为可到达。依据这一性质,我们建立一个行线段树,对于每一个障碍物,他的右边的点一定是无法从这一行的左面到达的,因此只需看上一行的状态。对于如果当前在第 i 行 , 如果
a
i
,
k
a_{i,k}
ai,k和
a
i
,
j
a_{i,j}
ai,j是障碍物,那么我们就找到第 i - 1 行中区间[j + 1 , k - 1]内最左边的能到达的点 x,那么第i行的 [x , k - 1]就是可到达的。此过程用线段树动态查询修改即可。
时间复杂度为(klogm)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
#define inf 0x3f3f3f3f
struct node1
{
int xx;
int yy;
}e[N*3];
struct node
{
int l,r,lz;
int num;
}tr[N*4];
void build(int i,int l,int r)
{
tr[i].r=r;
tr[i].l=l;
if(tr[i].r==tr[i].l)
{
tr[i].num = inf;
return;
}
int mid = (l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
tr[i].lz = -1;
tr[i].num = inf;
return;
}
void push_down(int i)
{
if(tr[i].lz!=-1)
{
tr[i<<1].lz=tr[i].lz;
tr[i<<1|1].lz=tr[i].lz;
int mid=(tr[i].l+tr[i].r)>>1;
if(tr[i].lz==1)
{
tr[i<<1].num=tr[i<<1].l*tr[i].lz;
tr[i<<1|1].num=tr[i<<1|1].l*tr[i].lz;
}
else
{
tr[i<<1].num=inf;
tr[i<<1|1].num=inf;
}
tr[i].lz=-1;
}
return;
}
void update(int i,int l,int r,int k)
{
if(tr[i].l>=l&&tr[i].r<=r)
{
if(k==1)
tr[i].num=tr[i].l*k;
else
tr[i].num=inf;
tr[i].lz=k;
return;
}
push_down(i);
if(tr[i<<1].r>=l)
{
update(i<<1,l,r,k);
}
if(tr[i<<1|1].l<=r)
{
update(i<<1|1,l,r,k);
}
tr[i].num = min(tr[i<<1].num,tr[i<<1|1].num);
return;
}
int query(int i,int l,int r)
{
if(tr[i].l>=l&&tr[i].r<=r)
{
return tr[i].num;
}
push_down(i);
int num=inf;
if(tr[i*2].r>=l)
{
num=min(num,query(i<<1,l,r));
}
if(tr[i<<1|1].l<=r)
{
num=min(num,query(i<<1|1,l,r));
}
return num;
}
bool cmp(node1 a,node1 b)
{
if(a.xx!=b.xx)
return a.xx<b.xx;
else
return a.yy<b.yy;
}
int main()
{
// freopen("1008.in","r",stdin);
// freopen("wa.out","w",stdout);
int t;
cin>>t;
while(t--)
{
memset(e,0,sizeof(e));
memset(tr,0,sizeof(tr));
long long ans = 0;
long long n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=k;i++)
{
scanf("%d %d",&e[i].xx,&e[i].yy);
}
for(int i=k+1;i<=k+n;i++)
{
e[i].xx=i-k;
e[i].yy=m+1;
}//增加末尾
for(int i=k+n+1;i<=k+n+n-1;i++)
{
e[i].xx=i-k-n+1;
e[i].yy=0;
}//增加开头
sort(e+1,e+k+2*n,cmp);
build(1,1,m);
if(e[1].xx==1&&e[1].yy!=(m+1))//初始化
{
update(1,1,e[1].yy-1,1);
update(1,e[1].yy,m,0);
}
else
{
update(1,1,m,1);
}
int tmpnum = 0;
for(int i=1;i<=k+2*n-1;i++)
{
// cout<<"pos:"<<e[i].xx<<" "<<e[i].yy<<" "<<tmpnum<<" "<<ans<<" "<<query(1,1,m)<<endl;
int l = 0;
int r = 0;
if(e[i].xx == e[i+1].xx)
{
if(e[i].yy!=0&&e[i].yy!=m+1)
{
tmpnum++;
update(1,e[i].yy,e[i].yy,0);
}
l = e[i].yy+1;
r = e[i+1].yy-1;
if(r<l)//相邻直接跳
continue;
int pos = query(1,l,r);
// cout<<l<<" "<<r<<" "<<pos<<endl;
if(pos==inf)//不存在1
{
update(1,l,r,0);
ans = ans + (r-l+1);
}
else
{
if(pos!=l)
update(1,l,pos-1,0);
update(1,pos,r,1);
ans = ans + (pos-l);
}
}
else//换行
{
ans = ans + tmpnum ;
tmpnum = 0;
}
}
printf("%lld\n",n*m-ans);
}
return 0;
}
/*
2
4 4 4
1 3
3 4
3 2
4 3
4 4 5
1 3
3 4
3 2
4 3
2 1
*/