AtCoder Beginner Contest 356 - VP记录
A - Subsegment Reverse
点击查看代码
#include<cstdio>
#include<numeric>
#include<algorithm>
using namespace std;
const int N=105;
int n,a[N],l,r;
int main()
{
scanf("%d%d%d",&n,&l,&r);
iota(a+1,a+n+1,1);
reverse(a+l,a+r+1);
for(int i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}
B - Nutrients
高桥出镜率 \(100 \%\)。
点击查看代码
#include<cstdio>
using namespace std;
const int N=105,M=105;
int n,m,a[M],x[N][M],b[M];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&x[i][j]);
b[j]+=x[i][j];
}
bool ans=true;
for(int i=1;i<=m;i++)
if(b[i]<a[i])
{
ans=false;
break;
}
printf("%s\n",ans?"Yes":"No");
return 0;
}
C - Keys
每次必有的暴力练习题。
点击查看代码
#include<cstdio>
using namespace std;
const int N=20,M=105;
int n,m,k;
int c[M],a[M][N],bt[M];
bool res[M];
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
scanf("%d",&c[i]);
for(int j=1;j<=c[i];j++)
{
scanf("%d",&a[i][j]);
bt[i]|=1<<(a[i][j]-1);
}
char str[5]; scanf("%s",str);
res[i]=str[0]=='o';
}
int ans=0;
for(int z=0;z<1<<n;z++)
{
bool is_ok=true;
for(int i=1;i<=m;i++)
if((__builtin_popcount(z&bt[i])>=k) != res[i])
{
is_ok=false;
break;
}
if(is_ok) ans++;
}
printf("%d\n",ans);
return 0;
}
D - Masked Popcount
找出 \([0,n]\) 中所有数二进制第 \(i\) 位上 \(1\) 的数量。
这是构成一个循环的,具体来说,例如 \(i=3\) 的时候,\([4,7],[12,15],[20,23],[28,31]\dots\) 的第 \(3\) 位上有 \(1\),即每 \(2^i\) 个数中有 \(2^{i-1}\) 个数第 \(i\) 位是 \(1\)。
所以我们找出循环的数量,然后剩下的一截判断是否在第 \(i\) 位上有 \(1\) 的数值区间内,若有就加上剩下的一块 \(1\)。
最后若 \(m\) 在某一位上有 \(1\),答案就要加上这一位上 \(1\) 的数量。
注意我代码里二进制最低位是第 \(0\) 位。
#include<cstdio>
using namespace std;
const int LogN=60,P=998244353;
long long n,m;
long long cnt[LogN+5];
int main()
{
scanf("%lld%lld",&n,&m);
long long ans=0;
for(int i=0;i<=LogN;i++)
{
long long t=n;
long long cycle=(t+1)>>(i+1);
cnt[i]+=cycle<<i;
t-=cycle<<(i+1);
if(t>=1ll<<i) cnt[i]+=t-(1ll<<i)+1;
if((m>>i)&1) ans=(ans+cnt[i])%P;
}
printf("%lld\n",ans);
return 0;
}
E - Max/Min
最恶心的一道,没有之一。
将 \(a\) 从小到大排序,答案转化为:
然后将答案分为 \(a_i=a_j\) 和 \(a_i \neq a_j\) 两种情况。对于第一种情况,直接用公式即可算,难点在于第二种。
对于 \(\lfloor\frac{a_i}{a_j}\rfloor\),因为除数和商不可能都大于 \(\sqrt{a_i}\),所以将情况分为除数小于等于 \(\sqrt{a_i}\) 和被除数小于等于 \(\sqrt{a_i}\),而对于后面一种情况,为避免重复,非常不建议枚举的时候直接判断被除数(我就是这么挂十发的),而应该采用判断“除数大于 \(\sqrt{a_i}\)”的判断方式,这样才可以保证不重不漏。
-
第一种情况:可能的商有 \(\sqrt{a_i}\) 种,当被除数、商一定时(\(\lfloor x \div y \rfloor = z\)),除数是一个确定的区间 \(\left[\lfloor\frac{x}{y+1}\rfloor+1,\lfloor\frac{x}{y}\rfloor\right]\),预处理出值域前缀和就可以直接算。
-
第二种情况:可能的除数有 \(\sqrt{a_i}\) 种,当被除数、除数一定时,商唯一确定。直接加就行。
注意找的时候均必须找 \(a_j\) 严格小于 \(a_i\),因为等于是在刚才单独计算的。
总的来说,我这种方法代码细节超多,不是很建议学。
时间复杂度 \(O(n \sqrt{n})\)
#include<cstdio>
#include<algorithm>
#pragma GCC optimize(2)
using namespace std;
namespace IO{
template<typename TYPE> void read(TYPE &x)
{
x=0; bool neg=false; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')neg=true;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^'0');ch=getchar();}
if(neg) {x=-x;} return;
}
template<typename TYPE> void write(TYPE x)
{
if(!x){putchar('0');return;} if(x<0){putchar('-');x=-x;}
static int sta[55]; int statop=0; while(x){sta[++statop]=x%10;x/=10;}
while(statop) {putchar('0'+sta[statop--]);} return;
}
} using namespace IO;
const int N=2e5+5,A=1e6+5;
int n,a[N],maxa;
long long ans=0;
int cnt[A];
#define qrange(l,r) (cnt[r]-cnt[(l)-1])
int main()
{
read(n);
for(int i=1;i<=n;i++)
{
read(a[i]);
cnt[a[i]]++;
maxa=max(maxa,a[i]);
}
for(int i=1;i<=maxa;i++)
{
if(cnt[i]) ans+=1ll*cnt[i]*(cnt[i]-1)/2;
cnt[i]+=cnt[i-1];
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
for(long long j=1;j*j<=a[i];j++) //枚举商
ans+=j*qrange(a[i]/(j+1)+1,min(a[i]/j,1ll*a[i]-1));
for(long long j=1;(a[i]/j)*(a[i]/j)>a[i];j++) //枚举除数
ans+=(a[i]/j)*qrange(j,min(j,1ll*a[i]-1));
}
write(ans);
return 0;
}
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/18540340