AtCoder Beginner Contest 392
AtCoder Beginner Contest 392
A - Shuffled Equation
给三个数,问是否存在两个数的乘积等于最后一个数。
枚举一下就行了。
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
int a,b,c;
cin>>a>>b>>c;
if(a*b==c||a*c==b||b*c==a)
puts("Yes");
else
puts("No");
}
B - Who is Missing?
给定M个数,每个数都在1到N中,且两两不同。
列出所有1到N中没有的数字。
用一个数组标记那些数字出现过即可。
#include<iostream>
using namespace std;
const int MAX=1010;
bool vis[MAX];
int n,m;
int main()
{
cin>>n>>m;
cout<<n-m<<endl;
for(int i=1;i<=m;++i)
{
int a;
cin>>a;
vis[a]=true;
}
for(int i=1;i<=n;++i)
if(!vis[i])
cout<<i<<' ';
cout<<endl;
return 0;
}
C - Bib
有n个人,第i个人身上有一个数字,并且他正在看着第个人。
对于每个,回答身上的数字为的人正在看的那个人的身上的数字。
保证每个两两不同。
格外维护一个数组,表示身上数字为的人对应的编号是多少,假设这个数组是。
那么身上数字为的人,看着的人的编号是,那么这个人身上的数字就是
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 300300
int n;
int P[MAX],Q[MAX],id[MAX];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&P[i]);
for(int i=1;i<=n;++i)
scanf("%d",&Q[i]);
for(int i=1;i<=n;++i)
id[Q[i]]=i;
for(int i=1;i<=n;++i)
printf("%d ",Q[P[id[i]]]);
puts("");
return 0;
}
D - Doubles
有个骰子,第个骰子有面,上面的数字分别是,投掷骰子的时候,每个面等概率出现。
选择两个骰子并投掷他们,回答这两个骰子投掷出相同数字的概率的最大值。
考虑如何计算两个骰子投出相同数字的概率,实际上就是每次两个骰子各枚举一个面,统计数字相同的数量,然后除以两个骰子的面数的乘积。
这样单次的复杂度是。
如何优化这个过程呢?对于其中一个骰子,用一个桶数组维护每个数字出现的数量,这样子另一个骰子只需要扫一面,累加桶中对应数字出现的次数即可。单次的复杂度可以优化到。
于是就可以暴力枚举两两骰子了。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 100100
vector<int> A[MAX];
int K[MAX];
int n;
double maxP=0;
int cnt[MAX];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&K[i]);
int x;
for(int j=1;j<=K[i];++j)
{
scanf("%d",&x);
A[i].push_back(x);
}
}
for(int i=1;i<=n;++i)
{
for(int j=0;j<K[i];++j)
cnt[A[i][j]]++;
for(int j=i+1;j<=n;++j)
{
long long tot=0;
for(int k=0;k<K[j];++k)
tot+=cnt[A[j][k]];
maxP=max(maxP,1.0*tot/(1.0*K[i])/(1.0*K[j]));
}
for(int j=0;j<K[i];++j)
cnt[A[i][j]]--;
}
printf("%.10lf\n",maxP);
return 0;
}
E - Cables and Servers
有个点,条双向边的图。
每次可以选择一个点,选择其连接的一条边,然后改变这个边连接的点。
问最少多少次操作之后图联通。
很显然先做生成树,剩下的联通块内的连边把其它联通块连上就行了。
用一个并查集维护联通块,非树边单独拎出来处理连接其它联通块即可。
#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
using namespace std;
#define MAX 200200
int n,m;
vector<pair<pair<int,int>,int> > A;
set<int> S;
int f[MAX];
int getf(int x){return (x==f[x])?x:f[x]=getf(f[x]);}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)f[i]=i;
for(int i=1;i<=m;++i)
{
int a,b;
scanf("%d%d",&a,&b);
if(getf(a)==getf(b))
A.push_back(make_pair(make_pair(a,b),i));
else
f[getf(a)]=getf(b);
}
int ans=-1;
for(int i=1;i<=n;++i)
if(getf(i)==i)
S.insert(i),ans+=1;
printf("%d\n",ans);
for(int i=0,l=A.size();i<l&&ans>0;++i,--ans)
{
int x=A[i].first.first,y=A[i].first.second;
int id=A[i].second;
int fa=getf(x);
S.erase(fa);
int nt=*S.begin();
printf("%d %d %d\n",id,y,nt);
f[fa]=nt;
}
return 0;
}
F - Insert
一开始有一个空的数组,执行N次操作。
第次操作是:将插入到中,使其成为第个元素。
输出最终的序列。
首先倒着插入,那么插入的位置其实就是从前往后空着的第个位置,这个可以很容易的使用一个前缀和数据结构+二分处理。
线段树+线段树上二分可以做到,树状数组+二分可以做到。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 500500
int n,p[MAX],a[MAX];
int t[MAX];
int lb(int x){return x&(-x);}
void add(int x,int a){while(x<=n)t[x]+=a,x+=lb(x);}
int sum(int x){int ret=0;while(x)ret+=t[x],x-=lb(x);return ret;}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&p[i]);
for(int i=1;i<=n;++i)add(i,1);
for(int i=n;i;--i)
{
int l=1,r=n,ans1=0,ans2=0,ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(sum(mid)<=p[i])ans1=mid,l=mid+1;
else r=mid-1;
}
l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(sum(mid)<p[i])l=mid+1;
else r=mid-1,ans2=mid;
}
if(!a[ans1])ans=ans1;
else ans=ans2;
a[ans]=i;
add(ans,-1);
}
for(int i=1;i<=n;++i)
printf("%d ",a[i]);
puts("");
return 0;
}
G - Fine Triplets
对于三个数,其中,如果满足,那么称为一个好的三元组。
给定个不同的正整数,问能够构成多少个好的三元组。
换个思路,B-A=C-B,所以2B=A+C,发现值域很小,所以A+C这个东西的出现次数可以使用多项式卷积很快速的计算出来。
所以离散数字转成值域上的多项式,用NTT计算卷积即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 2100100
#define MOD 998244353
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
int W[MAX],r[MAX];
void NTT(int *P,int opt,int len)
{
int l=0,N;for(N=1;N<len;N<<=1)++l;
for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
for(int i=1;i<N;i<<=1)
{
int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
for(int j=0,p=i<<1;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
}
}
if(opt==-1)
{
reverse(&P[1],&P[N]);
for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
}
}
int n,a[MAX];
int b[MAX],c[MAX];
long long ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
int L=0;
for(int i=1;i<=n;++i)
b[a[i]]=1,L=max(L,a[i]);
int N;for(N=1;N<=L+L;N<<=1);
NTT(b,1,N);
for(int i=0;i<N;++i)c[i]=1ll*b[i]*b[i]%MOD;
NTT(c,-1,N);
for(int i=1;i<=n;++i)
ans+=(c[a[i]*2]-1)/2;
cout<<ans<<endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-02-09 AVL Tree
2018-02-09 【BZOJ2843】极地旅行社(Link-Cut Tree)
2018-02-09 【BZOJ1997】Planar(2-sat)
2018-02-09 【BZOJ2134】单位错选(数学期望,动态规划)
2018-02-09 【BZOJ1030】文本生成器(AC自动机,动态规划)
2018-02-09 【BZOJ3160】万径人踪灭(FFT,Manacher)
2018-02-09 【BZOJ2004】公交线路(动态规划,状态压缩,矩阵快速幂)