打到了Rank 33Rank\ 33Rank 33,还是不错的。
第一次顺顺畅畅没有WA地AK了一场ABC,写篇题解纪念一下……
Solution
T1
直接模拟即可。
T2
分别枚举每个点并用已给的公式算出其与原点的距离,然后统计距离不大于kkk的点数即可。
建议用long doublelong\ doublelong double存储。
T3
暴力即可,考虑如何快速判断kkk个111时其是否能被nnn整除。
我们可以维护一个值,即当前这么多111组成的数模nnn的值。考虑在末尾加上一个111后,原数xxx变为了10x+110x+110x+1,那么模数也乘101010加111,即模数可以O(1)O(1)O(1)维护。
当模数为000时显然满足要求,立即输出并结束即可。注意当位数达到一定量的时候,应跳出循环输出−1-1−1。
T4
显然,满足要求的条件是,左边清一色的RRR且右边清一色的WWW。
于是,我们算出WWW的数量为cntcntcnt,在原字符串的最后cntcntcnt位中数出不为WWW的字符数量,即为答案。
注意,这种做法的正确性在于,在后面cntcntcnt位中,每遇到一个不是WWW的字符就应当与前面n−cntn-cntn−cnt个字符中一个不为RRR的字符交换,此时满足要求且决策最优。
T5
首先,思考这样一个问题: 如果一个块的长度为xxx,每次把它劈成两个块,最终要求任何一个块的长度均不超过lenlenlen,求最少劈的次数。
我们可以贪心地操作,即对于长木块,每次都劈掉一块长度为lenlenlen的,直到满足要求,此时决策最优且劈的次数为⌈ab⌉−1\lceil \frac a b \rceil -1⌈ba⌉−1。
回到原题,可以发现是一道有单调性的二分题(最小值最大, 最大值最小这种字眼应该很敏感吧)。我们每次判断: 能否让块的最大值最终不大于midmidmid且劈的次数不多于kkk次。判断的方式就是判断∑i=1n(⌈aimid⌉−1)\sum_{i=1}^n (\lceil \frac {a_i} {mid} \rceil -1)∑i=1n(⌈midai⌉−1)的值是否超过了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×500000≈3.6×108,以及它约为222的常数,在2000ms2000ms2000ms的时限下易被卡,所以果断放弃莫队。
此时,我们可以将询问按右端点排序,维护一个序列bbb,其中bib_ibi表示第iii个位置当前对答案的贡献。显然,如果我们目前扫到了第999个位置,第444个位置与它的值一样;那么,对于之后所有右端点在999号位置及999号位置之后的询问中,第444个位置均不可能产生贡献。同时,第999个位置暂时产生了111的贡献。
于是,我们同时维护一个数组lastlastlast,lastilast_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_i∑i=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;
}
本文详细解析了六道ACM竞赛题目,涵盖直接模拟、枚举计算、暴力解法、字符串处理、二分查找和树状数组应用等关键算法。通过实战案例,深入浅出地介绍了算法设计思路和代码实现技巧。


被折叠的 条评论
为什么被折叠?



