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个人身上有一个数字Qi,并且他正在看着第Pi个人。

对于每个i,回答身上的数字为i的人正在看的那个人的身上的数字。

保证每个Qi,Pi两两不同。

格外维护一个数组,表示身上数字为i的人对应的编号是多少,假设这个数组是id

那么身上数字为i的人,看着的人的编号是P[id[i]],那么这个人身上的数字就是Q[P[id[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

N个骰子,第i个骰子有Ki面,上面的数字分别是Ai,1,...,Ai,ki,投掷骰子的时候,每个面等概率出现。

选择两个骰子并投掷他们,回答这两个骰子投掷出相同数字的概率的最大值。

考虑如何计算两个骰子投出相同数字的概率,实际上就是每次两个骰子各枚举一个面,统计数字相同的数量,然后除以两个骰子的面数的乘积。

这样单次的复杂度是O(Ki×Kj)

如何优化这个过程呢?对于其中一个骰子,用一个桶数组维护每个数字出现的数量,这样子另一个骰子只需要扫一面,累加桶中对应数字出现的次数即可。单次的复杂度可以优化到O(Ki+Kj)

于是就可以暴力枚举两两骰子了。

#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

n个点,m条双向边的图。

每次可以选择一个点,选择其连接的一条边,然后改变这个边连接的点。

问最少多少次操作之后图联通。

很显然先做生成树,剩下的联通块内的连边把其它联通块连上就行了。

用一个并查集维护联通块,非树边单独拎出来处理连接其它联通块即可。

#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次操作。

i次操作是:将i插入到A中,使其成为第Pi个元素。

输出最终的序列。

首先倒着插入,那么插入的位置其实就是从前往后空着的第Pi个位置,这个可以很容易的使用一个前缀和数据结构+二分处理。

线段树+线段树上二分可以做到O(nlogn),树状数组+二分可以做到O(nlog2n)

#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

对于三个数A,B,C,其中A<B<C,如果满足BA=CB,那么称(A,B,C)为一个好的三元组。

给定N个不同的正整数,问能够构成多少个好的三元组。

换个思路,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;
}
posted @   小蒟蒻yyb  阅读(60)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的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】公交线路(动态规划,状态压缩,矩阵快速幂)
点击右上角即可分享
微信分享提示