【题解】CF1986G1

翻译

原题链接
在这里插入图片描述

思路

  数据很大,显然两边同时处理,所以要从 p i i \frac{p_{i}}{i} ipi下手。
  要让 p i i ∗ p j j \frac{p_{i}}{i} * \frac{p_{j}}{j} ipijpj是整数,每个 p i i \frac{p_{i}}{i} ipi都可以提供分子上的若干个因子,又需要别人提供分母上的所有因子。
  对于每一个 p i p_{i} pi,记 d = g c d ( p i , i ) d=gcd(p_{i}, i) d=gcd(pi,i) P = p i d , Q = i d P=\frac{p_{i}}{d},Q=\frac{i}{d} P=dpiQ=di,我们记录所有二元对 ( x , y ) (x, y) (x,y),满足 x ∣ P x|P xP y = Q y=Q y=Q,意义是它能提供因子 x x x,又需要因子 y y y
  能和 p i i \frac{p_{i}}{i} ipi配对的数量就是之前记录的 ( y , x ) (y, x) (y,x)的数量。

  由于要分解因子和使用 m a p map map,时间复杂度为 O ( n n l o g n ) O(n \sqrt{n}logn) O(nn logn)

代码

#include<bits/stdc++.h>
#define N 100005
using namespace std;
map<pair<int, int> , int> m;
int t, n, p[N];
int gcd(int x, int y) {
	if(y == 0) return x;
	return gcd(y, x%y);
}
int main() {
	cin>>t;
	while(t--) {
		cin>>n;
		long long ans = 0;
		m.clear();
		for(int i=1;i<=n;i++) scanf("%d", p+i);
		for(int i=1;i<=n;i++) {
			int d = gcd(i, p[i]);
			int P = p[i] / d, Q = i / d;
			for(int j=1;j*j<=P;j++) {
				if(P % j == 0) {
					ans += m[{Q, j}];
					if(j * j != P) {
						ans += m[{Q, P/j}];
					}
				}
			}
			
			for(int j=1;j*j<=P;j++) {
				if(P % j == 0) {
					m[{j, Q}] ++;
					if(j * j != P) {
						m[{P/j, Q}] ++;
					}
				}
			}
		}
		cout<<ans<<endl;
	}
}

关于G2的想法

  先把G1的代码改一改数据范围丢进去再说,结果预料中的TLE没出现,内存先爆了。这也正常,最多花多少内存不会算 ,但肯定是map花了太多内存。然后想了点办法:

  (1)用swap方法彻底释放map的内存。
  (2)注意到只有存在一个 i i i需要对应的 Q Q Q,我们才在 x = Q 且 x ∣ P j x=Q且x|P_{j} x=QxPj时记录下 ( x , y ) (x, y) (x,y)。而这样的 Q Q Q最多只有 n n n个。所以在记录前判断一下。
  (3)在读取map中的数据之前判断一下数据是否在map中。

  效果不错,MLE变TLE了,也就是说时间复杂度也得改,这个 l o g log log不能要。

  然后map其实就是用来计数的,所以我想到了hash,手写肯定不行,又得MLE,于是用上了unordered_map。

  然后调了半天,先是C++版本不行,然后是没有内置的关于pair的映射,要重定义一下,最后好不容易编译过了,还是TLE。

  后面试了一下开long long避免哈希冲突地太厉害,没什么用。

  总之就是裂开了,代码放下面,有哪位大佬能救一下的或者指出原因的欢迎评论QwQ。

#include<bits/stdc++.h>
#define N 500005
using namespace std;
namespace std {
    template<> struct hash<pair<int, int> > {
        size_t operator()(const pair<int, int>& p) const {

            return hash<int>()(p.first) ^ hash<int>()(p.second);
        }
    };
}
unordered_map<pair<int, int> , int> m;
int t, n, p[N];
bool needed[N];
inline int gcd(int x, int y) {
	if(y == 0) return x;
	return gcd(y, x%y);
}
int main() {
	cin>>t;
	while(t--) {
		scanf("%d", &n);
		long long ans = 0;
		unordered_map<pair<int, int>, int>().swap(m);    // 可以清空内存 
		for(int i=1;i<=n;i++) scanf("%d", p+i);
		for(int i=1;i<=n;i++) {
			int d = gcd(i, p[i]);
			int P = p[i] / d, Q = i / d;
			needed[Q] = true;
		}
		for(int i=1;i<=n;i++) {
			int d = gcd(i, p[i]);
			int P = p[i] / d, Q = i / d;
			for(int j=1;j*j<=P;j++) {
				if(P % j == 0) {
					if(m.find({Q, j}) != m.end()) ans += m[{Q, j}];
					if(j * j != P) {
						if(m.find({Q, P/j}) != m.end()) ans += m[{Q, P/j}];
					}
				}
			}
			
			for(int j=1;j*j<=P;j++) {
				if(P % j == 0) {
					if(needed[j]) m[{j, Q}] ++;
					if(j * j != P) {
						if(needed[P/j]) m[{P/j, Q}] ++;
					}
				}
			}
			
		}
		
		for(int i=1;i<=n;i++) {
			int d = gcd(i, p[i]);
			int P = p[i] / d, Q = i / d;
			needed[Q] = false;
		}
		printf("%d\n", ans)
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值