CF1822G2 - Magic Triples

比较好的题目,别的不说,G1 对 G2 有着不错的启发性。

首先,因为 b>0,ak109,所以 b 不可能超过 a

考虑对 b 分类讨论,设置一个阈值 B,先处理 b=1 的情况,其实就是取三个相同的数然后排列,可以比较简单的排序之后做到 O(n)

接着手写一个哈希表用来存所有 ai 出现次数。

然后考虑 1<bB,这种情况下我们可以遍历枚举 i,然后在哈希表中查询 baib2ai 出现的次数乘起来,注意不要乘上 ai 出现的次数,因为每个 ai 都会贡献一次。

然后分析 b>B 的情况,我们发现这时候因为 ak109,所以 aj109/B,那么我们就先枚举 aj,然后对于满足条件的 aj 枚举因子作为 b,一共有大约 109/B 个。对于其所有大于 b 的因子,都尝试将其作为 b,找到 ai/bbai 的出现次数乘起来即可。

B 为对 b 分治的长度,复杂度为 O(nB+namaxB)

此时取 B=amax1/3=1000 最优,足以通过此题。

const int N=200005,A=1000000000,B=1000;
ll n,a[200005];
struct hash_table{
	#define S 19198100
	vector<int>used;
	int sz=0,hd[S+5],id[N],nxt[N],w[N];
	inline void ins(int k){
		int u=k%S;
		for(int i=hd[u];i;i=nxt[i])if(id[i]==k)return (void)(w[i]++);
		++sz,nxt[sz]=hd[u],w[sz]=1,id[sz]=k,hd[u]=sz;
		used.push_back(u);
	}
	inline int qry(int k){
		for (int i=hd[k%S];i;i=nxt[i])if(id[i]==k)return w[i];
		return 0;
	}
	inline void flush(){
		sz=0;
		for(int i:used)hd[i]=0;
		used.clear();
	}
}h;
inline void solve(){
	cin>>n;
	rp(i,n)cin>>a[i];
	h.flush();
	rp(i,n)h.ins(a[i]);
	ll ans=0;
	map<ll,ll>mp;
	rp(i,n)mp[a[i]]++;
	for(auto i:mp)ans+=i.second*(i.second-1)*(i.second-2);
	rep(i,2,B){
		rp(j,n)if(a[j]*i*i<=A){
			ans+=h.qry(a[j]*i)*h.qry(a[j]*i*i);
		} 
	}
	rep(i,1,n)if(a[i]<=A/B){
		for(ll j=1;j*j<=a[i];j++)if(a[i]%j==0){
			if(a[i]*j<=A&&j>B)ans+=h.qry(a[i]/j)*h.qry(a[i]*j);
			if(a[i]*(a[i]/j)<=A&&a[i]/j>B)ans+=h.qry(j)*h.qry(a[i]*(a[i]/j));
		}
	}cout<<ans<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin>>t;
	rd(_,t)solve();
	return 0;
}
//Crayan_r

这个做法看上去比较优美,但你仔细一想它还是有那么一丝神头鬼脸。毕竟如果不出这个 G1,就很难把 103106 想到,而这个看上去就带着一丝 nt 的数据范围偏偏就是卡满了的 2108,你不拿这个 a1/3,拿个什么 a1/4 上去分析都要当场 GG。

那么就平心而论,续写这个还不完美的句号。

我们发现,最大数其实就是 b2ai

考虑为什么我们可以根号枚举因子,因为两数相乘为 a 必有一个数小于等于 a。而现在是三个数相乘,则其中的最小数就必然小于等于 a3。我们就考虑对当前的 a 枚举这个最小数 c

每出来一个数之后,我们先尝试拿它做 b,为了不重复算,我们不算 a/b/b<b 的情况。然后就是用 a/ba/b/b 进行贡献。

接着尝试拿他做 ai,直接对 a/c 开方,注意接着要判断是否恰好乘起来是 a,然后如果 a/c<c,就直接用 a/ca/cc 贡献一次。也就可以了。

const int N=200005,A=1000000000,B=1000;
ll n,a[200005];
struct hash_table{
	#define S 19198100
	vector<int>used;
	int sz=0,hd[S+5],id[N],nxt[N],w[N];
	inline void ins(int k){
		int u=k%S;
		for(int i=hd[u];i;i=nxt[i])if(id[i]==k)return (void)(w[i]++);
		++sz,nxt[sz]=hd[u],w[sz]=1,id[sz]=k,hd[u]=sz;
		used.push_back(u);
	}
	inline int qry(int k){
		for (int i=hd[k%S];i;i=nxt[i])if(id[i]==k)return w[i];
		return 0;
	}
	inline void flush(){
		sz=0;
		for(int i:used)hd[i]=0;
		used.clear();
	}
}h;
inline void solve(){
	cin>>n;
	rp(i,n)cin>>a[i];
	h.flush();
	rp(i,n)h.ins(a[i]);
	ll ans=0;
	sort(a+1,a+1+n);
	vt<pll>v;
	rp(i,n){
		if(!v.size()||v.back().first!=a[i])v.pb({a[i],1});
		else v.back().second++;
	}
	for(auto ix:v){
		ll f=ix.first,c=ix.second;
		ans+=c*(c-1)*(c-2);
		for(ll b=1;b*b*b<=f;b++)if(f%b==0){
			if(b>1&&f%(b*b)==0){
				if(f/(b*b)<b)continue;
				ans+=c*h.qry(f/b)*h.qry(f/(b*b));
			}
			ll k=(ll)sqrt(f/b);
			if(b<k&&b*k*k==f){
				ans+=c*h.qry(b*k)*h.qry(b);
			}
		}
	}cout<<ans<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin>>t;
	rd(_,t)solve();
	return 0;
}
posted @   jucason_xu  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示