A - Shuffled Equation

显然只有最大值可能被相乘得到,所以对\(a\)从小到大排序,判断\(a[0]\times a[1]=a[2]\)是否成立即可。

时间复杂度\(O(1)\)。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int a[3];
signed main(){
cin>>a[0]>>a[1]>>a[2];
sort(a,a+3);
cout<<(a[0]*a[1]==a[2]?"Yes\n":"No\n");
return 0;
}

B - Who is Missing?

用桶记录每个数的选取情况,枚举\(1\sim n\)所有数,输出所有未被选择的数即可。

时间复杂度\(O(n)\)。

点击查看代码
#include<bits/stdc++.h>
#define N 1010
using namespace std;
int n,m,cnt;
bitset<N> vis;
signed main(){
cin>>n>>m;
for(int i=1,a;i<=m;i++) cin>>a,vis[a]=1;
for(int i=1;i<=n;i++) if(!vis[i]) cnt++;
cout<<cnt<<"\n";
for(int i=1;i<=n;i++) if(!vis[i]) cout<<i<<" ";
return 0;
}

C - Bib

题目是让我们对于每个\(i\),求穿\(i\)号围兜的人盯着的人穿几号围兜。

用\(b[i]\)表示穿\(i\)号围兜的人,我们在输入的过程中处理出来。则根据上面的分析,第\(i\)个答案为\(q[p[b[i]]]\)。

点击查看代码
#include<bits/stdc++.h>
#define N 300010
using namespace std;
int n,p[N],q[N],b[N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>p[i];
for(int i=1;i<=n;i++) cin>>q[i],b[q[i]]=i;
for(int i=1;i<=n;i++) cout<<q[p[b[i]]]<<" ";
return 0;
}

D - Doubles

暴力枚举是\(O(n^2 V)\)的,其中\(V=10^5\)表示值域。考虑优化。

实际上对于我们枚举的\((i,j)\)这对骰子,仅需枚举其中一个骰子可能的点数即可,并不需要把\(1\sim 10^5\)的点数都枚举一遍。

分析一下时间复杂度:\(O(\sum\limits_{i,j}k[i])=O(\sum\limits_j S)=O(nS)\),其中\(S=\sum k=10^5\)。

点击查看代码
#include<bits/stdc++.h>
#define N 105
#define S 100010
using namespace std;
int n;
unordered_set<int> se[N];
double p[N][S],maxx;
signed main(){
cin>>n;
for(int i=1,m,x;i<=n;i++){
cin>>m;
double tmp=1.0/m;
for(int j=1;j<=m;j++)
cin>>x,p[i][x]+=tmp,se[i].insert(x);
}
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++){
double ans=0;
for(int k:se[i])
ans+=p[i][k]*p[j][k];
maxx=max(maxx,ans);
}
}
cout<<fixed<<setprecision(18)<<maxx;
return 0;
}

悄悄告诉你:由于此题玄学的数据,\(O(n^2 V)\)的暴力也可以过(Link)。

E - Cables and Servers

容易发现,最优操作下,每操作一次连通块就减少\(1\)。所以最少操作次数就是整张图的连通块个数\(-1\)。

再考虑如何输出方案。显然最优操作下,每个操作都必须合并\(2\)个连通块。换句话说,不能因为我们动用某条边,就使得原来的一整个连通块断成两半。

所以我们不妨规定,只动用原图生成树之外的边。

这样做法就出来了,我们对原图跑生成树,遍历生成树外的边,每条边选取其中一个端点,连向其他连通块中的任意一个即可。

时间复杂度\(O(n\alpha(n))\)。

点击查看代码
#include<bits/stdc++.h>
#define N 200010
#define M 200010
using namespace std;
int n,m,fa[N],to[M];
struct edge{int to,num;};
bitset<M> flg;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
unordered_set<int> se;
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v,to[i]=u;//to[i]=v 也可以
u=find(u),v=find(v);
if(u!=v) fa[u]=v,flg[i]=1;
}
for(int i=1;i<=n;i++) se.insert(find(i));
cout<<se.size()-1<<"\n";
for(int i=1;i<=m;i++){
if(se.size()==1) break;
if(flg[i]) continue;
auto it=se.begin();
if((*it)==find(to[i])) it++;//避免自己连向自己
fa[*it]=fa[to[i]];
cout<<i<<" "<<to[i]<<" "<<(*it)<<"\n";
se.erase(it);
}
return 0;
}
另一种实现(更简洁)
#include<bits/stdc++.h>
#define N 200010
#define M 200010
using namespace std;
int n,m,fa[N],to[M],ans;
struct edge{int to,num;};
bitset<M> flg;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
signed main(){
cin>>n>>m,ans=n-1;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v,to[i]=u;
u=find(u),v=find(v);
if(u!=v) fa[u]=v,flg[i]=1,ans--;
}
cout<<ans<<"\n";
for(int i=1,k=1;i<=m;i++){
if(flg[i]) continue;
while(k<=n&&find(k)==find(to[i])) k++;
if(k>n) break;
fa[fa[k]]=fa[to[i]];
cout<<i<<" "<<to[i]<<" "<<k<<"\n";
}
return 0;
}

F - Insert

看到题面首先想到平衡树之类的结构来模拟,又不想实现平衡树了,就想到用rope来维护序列,算了一下时间在\(O(n\sqrt n)\approx 3.5\times 10^8\),交上去搏一把,很不幸\(\tt TLE\times 4\)(Link)。

ropepb_ds库的一个分支,底层实现是块状链表,可以支持\(O(\sqrt n)\)的插入、删除等操作,从c++11开始受支持,具体用法可以自行搜索)

考虑除平衡树外的解法。

我们反过来考虑每个操作,初始定义\(A'=(1,2,\dots,n)\),对于\(i=n,n-1,\dots,1\),依次从\(A'\)中删除\(A'[P[i]]\),并记录\(B[i]\)为此次删除的数。

不难发现\(B[i]\)的另一个含义就是:第\(i\)个加入的元素最终的位置。

因此,对于\(i\in[1,n]\),令\(A[B[i]]=i\),即可得到答案\(A\)。


如何快速取到\(A'\)的第\(P[i]\)个元素?

我们可以定义\(T\)数组,初始全为\(1\)。删除某个元素\(x\),相当于令\(T[x]=0\)。

找第\(x\)个元素的实际下标,就相当于找使得\((\sum\limits_{i=1}^p T[i])\ge x\)的最小\(p\),可以使用树状数组上倍增来解决。

时间复杂度\(O(n\log n)\)。

点击查看代码
#include<bits/stdc++.h>
#define N 500010
using namespace std;
int n,p[N],sum[N],a[N];
inline int lowbit(int x){return x&-x;}
void add(int x,int k){for(;x<=n;x+=lowbit(x)) sum[x]+=k;}
int solve(int x){
int p=0,s=0;
for(int i=20;i>=0;i--)
if(p+(1<<i)<=n&&s+sum[p+(1<<i)]<x)
p+=(1<<i),s+=sum[p];
return p+1;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>p[i],add(i,1);
for(int i=n;i>=1;i--){
int ps=solve(p[i]);
a[ps]=i,add(ps,-1);
}
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
return 0;
}

G - Fine Triplets

对于\(1,2,\dots,n\)这样的数据,答案数量将达到\(n^2\)级别,无法逐个计数。

考虑\(O(n)\)来枚举三元组\((a,b,c)\)的中间值\(b\),由于\(c-b=b-a\iff a+c=2b\),所以它对答案的贡献即为和为\(2b\)的二元组(无序)个数。

如何快速判断和为\(k\)的二元组个数,我们可以想到使用生成函数。

比如对于集合\(S=\{1,2,3,5\}\),考虑构造如下式子:

\[(x^1+x^2+x^3+x^5)(x^1+x^2+x^3+x^5)=x^2+2x^3+3x^4+2x^5+3x^6+2x^7+2x^8+x^{10}
\]

不难发现我们构造的式子的\(k\)次项系数就代表和为\(k\)的二元组(有序)个数。比如\(2x^7=x^2x^5+x^5x^2\),意味着我们可以从左边选取\(2\),右边选取\(5\)来组成\(7\);或者左边选\(5\),右边选\(2\)来组成\(7\)。共有这\(2\)种选法。

上式的展开过程需要使用多项式乘法,可以使用FFT/NTT做到\(O(m\log m)\)的时间复杂度,其中\(m\)表示最高次项的次数,这里为值域\(V\)。

在这之后,我们就可以\(O(1)\)查询和为\(2b\)的二元组(有序)个数\(s\)了,对答案的贡献为\(\lfloor \frac{s-1}{2}\rfloor\),之所以要减去\(1\),是因为我们要除去两边同时取\(b\)的选法;除以\(2\)是因为是无序的。

总时间复杂度\(O(V\log V+n)\)。

点击查看代码(FFT)
#include<bits/stdc++.h>
#define N 1048586
#define Pi 3.1415926535897932384626
using namespace std;
struct complx{
double x,y;
complx(double xx=0,double yy=0){x=xx,y=yy;}
}a[N<<1];
int n,l,r[N<<1],s[N],limit=1;
long long ans;
inline complx operator+(complx a,complx b){return complx(a.x+b.x,a.y+b.y);}
inline complx operator-(complx a,complx b){return complx(a.x-b.x,a.y-b.y);}
inline complx operator*(complx a,complx b){return complx(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
void fft(complx *a,int type){
for(int i=0;i<limit;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int mid=1;mid<limit;mid<<=1){
complx Wn(cos(Pi/mid),type*sin(Pi/mid));
for(int R=mid<<1,j=0;j<limit;j+=R){
complx w(1,0);
for(int k=0;k<mid;k++,w=w*Wn){
complx x=a[j+k],y=w*a[j+mid+k];
a[j+k]=x+y;
a[j+mid+k]=x-y;
}
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>s[i],a[s[i]].x++;
while(limit<=2e6) limit<<=1,l++;
for(int i=0;i<limit;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
fft(a,1);
for(int i=0;i<limit;i++) a[i]=a[i]*a[i];
fft(a,-1);
for(int i=1;i<=n;i++) ans+=((long long)(a[s[i]*2].x/limit+0.5)-1)/2;
cout<<ans<<"\n";
return 0;
}

[题解]AtCoder Beginner Contest 392(ABC392) A~G的更多相关文章

  1. 题解 AtCoder Beginner Contest 168

    小兔的话 欢迎大家在评论区留言哦~ AtCoder Beginner Contest 168 A - ∴ (Therefore) B - ... (Triple Dots) C - : (Colon) ...

  2. [题解] Atcoder Beginner Contest ABC 270 G Ex 题解

    点我看题 G - Sequence in mod P 稍微观察一下就会发现,进行x次操作后的结果是\(A^xS+(1+\cdots +A^{x-1})B\).如果没有右边那一坨关于B的东西,那我们要求 ...

  3. AtCoder Beginner Contest 220部分题(G,H)题解

    刚开始的时候被E题卡住了,不过发现是个数学题后就开始使劲推式子,幸运的是推出来了,之后的F题更是树形DP换根的模板吧,就草草的过了,看了一眼G,随便口胡了一下,赶紧打代码,毕竟时间不多了,最后也没打完 ...

  4. [题解] Atcoder Beginner Contest ABC 265 Ex No-capture Lance Game DP,二维FFT

    题目 首先明确先手的棋子是往左走的,将其称为棋子1:后手的棋子是往右走的,将其称为棋子2. 如果有一些行满足1在2右边,也就是面对面,那其实就是一个nim,每一行都是一堆石子,数量是两个棋子之间的空格 ...

  5. AtCoder Beginner Contest 154 题解

    人生第一场 AtCoder,纪念一下 话说年后的 AtCoder 比赛怎么这么少啊(大雾 AtCoder Beginner Contest 154 题解 A - Remaining Balls We ...

  6. AtCoder Beginner Contest 177 题解

    AtCoder Beginner Contest 177 题解 目录 AtCoder Beginner Contest 177 题解 A - Don't be late B - Substring C ...

  7. KYOCERA Programming Contest 2021(AtCoder Beginner Contest 200) 题解

    KYOCERA Programming Contest 2021(AtCoder Beginner Contest 200) 题解 哦淦我已经菜到被ABC吊打了. A - Century 首先把当前年 ...

  8. AtCoder Beginner Contest 184 题解

    AtCoder Beginner Contest 184 题解 目录 AtCoder Beginner Contest 184 题解 A - Determinant B - Quizzes C - S ...

  9. AtCoder Beginner Contest 173 题解

    AtCoder Beginner Contest 173 题解 目录 AtCoder Beginner Contest 173 题解 A - Payment B - Judge Status Summ ...

  10. AtCoder Beginner Contest 148 题解

    目录 AtCoder Beginner Contest 148 题解 前言 A - Round One 题意 做法 程序 B - Strings with the Same Length 题意 做法 ...

随机推荐

  1. 使用Spring AOP 和自定义注解统一API返回值格式

    摘要:统一接口返回值格式后,可以提高项目组前后端的产出比,降低沟通成本.因此,在借鉴前人处理方法的基础上,通过分析资料,探索建立了一套使用Spring AOP和自定义注解无侵入式地统一返回数据格式的方 ...

  2. Go语言new和make的区别

    一.简单说明 方法 作用 作用对象 返回值 new 分配内存 值类型和用户定义的类型 初始化为零值,返回指针 make 分配内存 内置引用类型(map,slice,channel) 初始化为零值,返回 ...

  3. ceph集群故障运维--持续更新

    一.PG处于异常状态active+undersized+degraded 部署环境: 自己搭建的3节点集群,集群共5个OSD,部署Ceph的RadosGW的服务时,副本默认设置为3,集群存放数据量少. ...

  4. 垃圾PTA:7-2 统计数字字符和空格

    本题要求编写程序,输入一行字符,统计其中数字字符.空格和其他字符的个数.建议使用switch语句编写. 输入格式:输入在一行中给出若干字符,最后一个回车表示输入结束,不算在内. 输出格式:在一行内按照 ...

  5. BeatifulSoup

    BeatifulSoup (1)介绍 Beautiful Soup是Python库,用于解析HTML和XML文档.它提供简单而强大的工具,帮助用户从网页中提取数据.通过查找元素.遍历文档树和处理编码问 ...

  6. HyperWorks一维单元创建与模型连接管理

    在HyperWorks的有限元分析中,一维单元是非常重要的概念.我们可以使用一维单元连接节点,或将不匹配的网格部件进行连接,进行载荷施加,以及用于建立焊接,螺栓,铆钉等等各类工程中经常运用的模型连接方 ...

  7. LiteLLM - 统一接口调用100+ LLM模型

    :bullet_train: LiteLLM LiteLLM 是一个统一的接口层,支持调用100+种大语言模型(LLM),包括Bedrock.Huggingface.VertexAI.Together ...

  8. PTA 4-1

    PTA    习题4-1 求奇数和 (15分) 本题要求计算给定的一系列正整数中奇数的和. 输入格式: 输入在一行中给出一系列正整数,其间以空格分隔.当读到零或负整数时,表示输入结束,该数字不要处理. ...

  9. java--Struts数据回显、模型驱动、防止表单重复提交

    Struts数据效验 表单数据的验证: 前台验证:主要是通过JS验证, 表单数据是否合法! 后台验证:通过后台java代码进行验证! Struts也提供了数据效验的方式! Struts数据效验, 通过 ...

  10. Codeforces Round #697 (Div. 3) ABCDE 题解

    久违的cf服务器爆炸场 A. Odd Divisor 思路:任何一个数都可以写成\(n = k2^m,其中k是一个奇数\),若k=1,那么n就一定是一个2的幂. view code #include& ...