Atcoder Beginner Contest 174(ABC174) 题解

本文详细解析了六道ACM竞赛题目,涵盖直接模拟、枚举计算、暴力解法、字符串处理、二分查找和树状数组应用等关键算法。通过实战案例,深入浅出地介绍了算法设计思路和代码实现技巧。

打到了Rank 33Rank\ 33Rank 33,还是不错的。

第一次顺顺畅畅没有WA地AK了一场ABC,写篇题解纪念一下……

Solution

T1

直接模拟即可。

T2

分别枚举每个点并用已给的公式算出其与原点的距离,然后统计距离不大于kkk的点数即可。

建议用long doublelong\ doublelong double存储。

T3

暴力即可,考虑如何快速判断kkk111时其是否能被nnn整除。

我们可以维护一个值,即当前这么多111组成的数模nnn的值。考虑在末尾加上一个111后,原数xxx变为了10x+110x+110x+1,那么模数也乘101010111,即模数可以O(1)O(1)O(1)维护

当模数为000时显然满足要求,立即输出并结束即可。注意当位数达到一定量的时候,应跳出循环输出−1-11

T4

显然,满足要求的条件是,左边清一色的RRR且右边清一色的WWW

于是,我们算出WWW的数量为cntcntcnt,在原字符串的最后cntcntcnt位中数出不为WWW的字符数量,即为答案。

注意,这种做法的正确性在于,在后面cntcntcnt位中,每遇到一个不是WWW的字符就应当与前面n−cntn-cntncnt个字符中一个不为RRR的字符交换,此时满足要求且决策最优。

T5

首先,思考这样一个问题: 如果一个块的长度为xxx,每次把它劈成两个块,最终要求任何一个块的长度均不超过lenlenlen,求最少劈的次数。

我们可以贪心地操作,即对于长木块,每次都劈掉一块长度为lenlenlen的,直到满足要求,此时决策最优且劈的次数为⌈ab⌉−1\lceil \frac a b \rceil -1ba1

回到原题,可以发现是一道有单调性的二分题(最小值最大, 最大值最小这种字眼应该很敏感吧)。我们每次判断: 能否让块的最大值最终不大于midmidmid且劈的次数不多于kkk次。判断的方式就是判断∑i=1n(⌈aimid⌉−1)\sum_{i=1}^n (\lceil \frac {a_i} {mid} \rceil -1)i=1n(midai1)的值是否超过了kkk,其中前者表示,分别考虑每个块至少要被劈的次数使得其产生的新块中不含大于midmidmid的块。

时间复杂度为O(nlog2n)O(nlog_2n)O(nlog2n)

T6

莫队解法的时间复杂度为O(nn)O(n \sqrt n)O(nn),经过简单的计算,发现50000×500000≈3.6×10850000×\sqrt {500000}≈3.6×10^850000×5000003.6×108,以及它约为222的常数,在2000ms2000ms2000ms的时限下易被卡,所以果断放弃莫队

此时,我们可以将询问按右端点排序,维护一个序列bbb,其中bib_ibi表示第iii个位置当前对答案的贡献。显然,如果我们目前扫到了第999个位置,第444个位置与它的值一样;那么,对于之后所有右端点在999号位置及999号位置之后的询问中,第444个位置均不可能产生贡献。同时,第999个位置暂时产生了111的贡献。

于是,我们同时维护一个数组lastlastlastlastilast_ilasti记录下iii这个数上一次出现的位置。若目前扫到了第iii个位置,根据之前说的,要将第iii个位置的贡献值加111;若lastailast_{a_i}lastai(上一次出现的位置)的值不是000,即更新过,那么lastailast_{a_i}lastai位置的贡献值要扣除111。一次对于[l,r][l,r][l,r]的询问,答案显然是∑i=lrbi\sum_{i=l}^r b_ii=lrbi,其中bib_ibi记录下了第iii个位置的贡献。

发现bbb数组涉及到单点修改以及区间查询,可以轻松用树状数组来维护。我们另外再维护一个指针jjj,指向马上需要解决的一个询问,如果该询问解决了就j++j++j++,跳到下一个询问。最后注意,我们先前已将询问按右端点进行了排序,最终输出要还原顺序

时间复杂度O(nlog2n)O(nlog_2n)O(nlog2n)

Code

T1

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n;

signed main()
{
	cin>>n;
	if (n>=30)  cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	
	return 0;
}

T2

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;

int n,d,ans=0;
double x,y;

signed main()
{
	cin>>n>>d;
	for (int i=1;i<=n;i++)
	{
		cin>>x>>y;
		
		double len=sqrt(x*x+y*y);
		if (len<=d)  ans++;
	}
	cout<<ans<<endl;
	
	return 0;
}

T3

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,ii=0,now=0;

signed main()
{
	cin>>n;
	while (ii<=80000000)
	{
		ii++;
		now=(now*10+7)%n;
		
		if (now==0)  return cout<<ii<<endl,0;
	}
	cout<<-1<<endl;
	return 0;
}

T4

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,cnt=0,ans=0;
char a[200005];

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)  cin>>a[i];
	for (int i=1;i<=n;i++)
	{
		if (a[i]=='W')  cnt++;
	}
	for (int i=n-cnt+1;i<=n;i++)
	{
		if (a[i]!='W')  ans++;
	}
	cout<<ans<<endl;
	
	return 0;
}

T5

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,k,ans=1e9+7;
int a[200005];

inline int up(int aa,int bb)
{
	if (aa%bb==0)  return aa/bb;
	else return (aa/bb)+1;
}

bool check(int len)//check函数
{
	int tot=0;
	for (int i=1;i<=n;i++)  tot=tot+up(a[i],len)-1;
	
	if (tot<=k)  return true;
	else return false;
}

int Binary_search(int l,int r)//递归式二分写法
{
	if (l==r||l+1==r)
	{
		if (check(l))  ans=min(ans,l);
		if (check(r))  ans=min(ans,r);
		return ans; 
	}
	int mid=(l+r)>>1;
	if (check(mid))
	{
		ans=min(ans,mid);
		return Binary_search(l,mid);
	}
	else return Binary_search(mid+1,r);
}

signed main()
{
	cin>>n>>k;
	for (int i=1;i<=n;i++)  cin>>a[i];
	
	cout<<Binary_search(1,1e9)<<endl;
	
	return 0;
}

T6

#include <bits/stdc++.h>
using namespace std;

int n,tmp,j=1;
int a[1000005],tree[2000005],last[1000005],ans[1000005];

struct node
{
	int rt,l,r;
}q[1000005];

inline int read()
{
	int s=0,w=1;
	char ch=getchar();
	
	while (ch<'0'||ch>'9')
	{
		if (ch=='-')  w=-w;
		ch=getchar();
	}
	while (ch>='0'&&ch<='9')
	{
		s=(s<<1)+(s<<3)+(ch^'0');
		ch=getchar();
	}
	return s*w;
}

bool cmp(node x,node y)
{
	return x.r<y.r;
}

inline int lowbit(int k)
{
	return k&(-k);
}

inline void change(int rt,int num)
{
	while (rt<=n)
	{
		tree[rt]+=num;
		rt+=lowbit(rt); 
	}
}

inline int query(int r)
{
	int tot=0;
	while (r>=1)
	{
		tot+=tree[r];
		r-=lowbit(r);
	}
	return tot;
}

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)  a[i]=read();
	
	cin>>tmp;
	for (int i=1;i<=tmp;i++)  q[i].l=read(),q[i].r=read(),q[i].rt=i;
	
	sort(q+1,q+tmp+1,cmp);
	
	for (int i=1;i<=n;i++)
	{
		change(i,1);
		if (last[a[i]])  change(last[a[i]],-1);
		while (i==q[j].r&&j<=tmp)  ans[q[j].rt]=query(q[j].r)-query(q[j].l-1),j++;
		
		last[a[i]]=i;
	}
	for (int i=1;i<=tmp;i++)  cout<<ans[i]<<endl;
	
	return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值