ABC392

终于进前一千了。

A - Shuffled Equation

人话:给定三个数 a,b,c,判断是否存在两个数乘积为第三个数。

数很小,if 判断一下,long long 也不用开。

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int a,b,c;
int main(){
	cin>>a>>b>>c;
	if(a*b==c)cout<<"Yes"<<endl;
	else if(b*c==a)cout<<"Yes"<<endl;
	else if(c*a==b)cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
} 

时间复杂度 O(1)

B - Who is Missing?

注意到值域很小,我们开一个桶。如果 i 出现过,则 fi1,反之为 0

然后按照 1n 的顺序检查那个桶里没数就行。

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n,m,x,t[1005],cnt;
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++)cin>>x,t[x]++;
	for(int i=1;i<=n;i++)if(!t[i])cnt++;
	cout<<cnt<<endl;
	for(int i=1;i<=n;i++)if(!t[i])cout<<i<<" ";
	return 0;
} 

时间复杂度 O(n)

C - Bib

挺绕的。

每个人有三个属性,编号,牌子(也就是 pi),看的人的编号(也就是 qi)。

假设 si 表示牌子上的数位 i 的人的编号。这个可以预处理建立映射关系。

那么根据定义,牌子为 i 的人编号为 si,看的人的编号为 qsi,看的人挂的牌子为 pqsi

主要是搞清楚编号,牌子,看的人之间的关系即可。

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N=3e5+10;
int n,p[N],q[N],s[N];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",q+i); 
	}
	for(int i=1;i<=n;i++){
		scanf("%d",s+i);
		p[s[i]]=i;
	}
	for(int i=1;i<=n;i++){
		printf("%d ",s[q[p[i]]]);
	}
	return 0;
} 

时间复杂度 O(n)

D - Doubles

咋还卡人精度呢?

先思考暴力,枚举两个骰子 i,j,枚举摇出来的数 x,则答案就是:

x=1cnti,xki×cntj,xkj

x=1cnti,x×cntj,xkikj

cnti,j 表示骰子 i 有多少个面是 j,上面的式子根据乘法原理和加法原理易得。

暴力会超时,注意值域很小,我们先枚举 i,然后开一个桶 tj 表示骰子 i 有多少个面为 j

接着我们枚举每一个 j<i,分别计算 i,j 的答案,具体做法是遍历 j 骰子的每一个面 x,然后让答案 ans 加上 tx。最后 anskikj

枚举 iO(n) 的,枚举每一个骰子 i 的面是 Ki,枚举 j,枚举 j 骰子的每一个面可以看成一个整体,是 O(SKi) 的,其中 S=Ki,意思是最多会将除了骰子 i 以外其他所有骰子的所有面都遍历一遍。

容易发现后两步合起来是 O(S)。这样做复杂度均摊下来是正确的。

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int N=1e5+10;
vector<int>v[105];
int n;
long long k[105],cnt[N];
long double res,ans;
long double fmax(long double a,long double b){
	return a<b?b:a;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",k+i);
		for(int j=1,x;j<=k[i];j++){
			scanf("%d",&x);
			v[i].push_back(x);
		}
	}
	for(int i=2;i<=n;i++){
		for(auto u:v[i])cnt[u]++;
		for(int j=1;j<i;j++){
			res=0;
			for(auto u:v[j])res=(double)(res+cnt[u]);
			res=(long double)(res/(k[i]*k[j]));
			ans=max(ans,res); 
		}
		for(auto u:v[i])cnt[u]--;
	}
	printf("%.12LF\n",ans);
	return 0;
}

时间复杂度 O(nS)

E - Cables and Servers

小清新题。

注意到 MN1,也就是说无论如何我们也可以给所有边的两个端点重新编号,让他联通。

我们先把原图建出来,这样的图是若干个不连通的图。在原图中,有一些边是无关紧要的,去掉这一条边对原图联通性没有影响。我们可以用这些边来连接其他的联通块。

我们思考这样一个过程:划分联通块,然后找出每个联通块有多少个无用的边,统计当前那个联通块 u 无用边最多,那个联通块 v 无用边次多,将 u 内的一条无用边的一个端点改到 v 上的端点,此时 u,v 就被合并了,v 的无用边也变成了 u 的无用边。因为 MN1,所以除了这个图仅剩一个联通块的情况,无论何时都有无用边

容易发现,此时 u,v 合并后的联通块又成为了无用边最多的联通块,假设有 k 个联通块,我们按照每个联通块内部无用边多少排序,然后依次将 2k 的联通块和 1 合并,这个算法就是正确的,容易发现,这样的操作此时为 k1

考虑构造方案,我们把每个联通块排好序后,将每个联通块内的无用边按照同样顺序放进数组里,然后从前往后一条一条边的用即可。

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int N=2e5+10;
int n,m,a[N],b[N],f[N],rt[N],k,rest[N],p,mk[N];
vector<int>vec[N];
int found(int x){
	return f[x]==x?x:f[x]=found(f[x]); 
}
bool cmp(int a,int b){
	return vec[a].size()>vec[b].size();
} 
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<=m;i++){
		scanf("%d %d",a+i,b+i);
		int u=found(a[i]);
		int v=found(b[i]);
		if(u!=v)f[u]=v;
		else mk[i]=1;
	}
	for(int i=1;i<=m;i++)if(mk[i])vec[found(a[i])].push_back(i);
	for(int i=1;i<=n;i++)if(found(i)==i)rt[++k]=i;
	printf("%d\n",k-1);
	sort(rt+1,rt+1+k,cmp);
	for(int i=1;i<=k;i++)for(auto j:vec[rt[i]])rest[++p]=j;
	for(int i=2;i<=k;i++){
		printf("%d %d %d\n",rest[i-1],a[rest[i-1]],rt[i]);
	}
	return 0;
}

时间复杂度 O(nlogn)

F - Insert

平衡树板子,蚌埠住了。

考虑无旋 treap(FHQ treap),每个节点上维护一个权值,和子树的大小,插入第 i 个数时,假设这个数排名为 k。split 时,将平衡树分成两颗大小分别为 k1ik 两颗平衡树,merge 时把新建的节点夹在中间合并即可,时间复杂度 O(nlogn)

点击查看代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
const int N=5e5+10;
int tot,root,n;
struct node{
    int l,r,size,dat,val;
}a[N];
int New(int s){
    a[++tot].val=s;
    a[tot].size=1;
    a[tot].dat=rand();
    return tot;
}
void update(int p){
    a[p].size=a[a[p].l].size+a[a[p].r].size+1;
}
void split(int p,int k,int &x,int &y){
    if(!p)return x=y=0,void();
    else if(a[a[p].l].size+1<=k)split(a[p].r,k-a[a[p].l].size-1,a[x=p].r,y);
    else split(a[p].l,k,x,a[y=p].l);
    update(p);
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(a[x].dat>a[y].dat)return a[x].r=merge(a[x].r,y),update(x),x;
    else return a[y].l=merge(x,a[y].l),update(y),y;
}
void insert(int s,int k){
    int x=0,y=0;
    split(root,k-1,x,y);
    root=merge(merge(x,New(s)),y);
}
void print(int p){
	if(!p)return;
	print(a[p].l);
	printf("%d ",a[p].val);
	print(a[p].r);
	return;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
    	int p;
    	scanf("%d",&p);
    	insert(i,p);
	}
	print(root);
    return 0;
}

多的可以去看平衡树模板。

G - Fine Triplets

好好好,都会 FFT/NTT 是吧。

首先 BA=CB 可以写作 2B=A+C,所以我们只需要求出有多少个数对 (A,C) 和为 2B

我们不妨开一个桶 ttx 表示 Sx 的出现次数。记 m=2B,上面说的这个问题答案即为 i=1m1titmi 的一半(因为求得是无序数对)。

这个玩意不就是一个卷积吗,FFT/NTT 可以 O(nlogn) 求出来。

?这不是卷积模板吗。

好吧,我不会卷积。。。

posted @   zuoqingyuan111  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示