题解:CF691F Couple Cover

原题链接

提供一种复杂度多一个 \(\log\) 的做法。

解析

我最初的想法是将询问离线下来,将球按权值排序,对于每个球 \(i\) 维护一个指针指向满足 \(a_i \cdot a_j \ge p\) 的第一个 \(j\),然后在处理询问的时候去移这个指针。

但是我们甚至无法接受在处理每个询问时遍历所有指针的复杂度。想到这一点,我就直接否掉了这个做法,但是其实可以继续做。

由于处理一个更大的 \(p\) 时并不一定是所有指针都需要移,所以考虑维护一个小根堆来存每个球当前指针指向的位置,按照对应值的乘积排序。但是这样总的指针移动次数依然可以达到 \(O(n ^ 2)\)

注意到对于权值相同的小球,指针指向的位置永远相同。所以直接去重,这样对于权值为 \(x\) 的小球,指针至多移动 \(\lceil\frac{p_{max}}{x}\rceil\) 次,根据调和级数的结论总移动次数就是 \(O(p \log n)\)。去重之后计算贡献需要预处理一个每种小球出现次数的后缀和。

总时间复杂度 \(O(p\log^2 n)\)

代码

/*
*/
#include<bits/stdc++.h>
#define eps 0.000001
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<ll,int> pii;
const int N = 3e6 + 5,M = 3.2e4 + 5;
ll a[N],cnt[N],suf[N],ans[N];
vector<ll> num;
struct cmp{
	bool operator()(pii p1,pii p2){
		return num[p1.first] * num[p1.second] > num[p2.first] * num[p2.second];
	}
};
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int n;
	cin>>n;
	
	for(int i=1;i<=n;i++){
		cin>>a[i];
		num.push_back(a[i]);
		cnt[a[i]]++;
	}
	for(int i=N - 2;i>=1;i--){
		suf[i] = suf[i + 1] + cnt[i];
	}
	int m;
	cin>>m;
	vector<pii> q;
	for(int i=1;i<=m;i++){
		int p;
		cin>>p;
		q.push_back({p,i});
	}
	sort(q.begin(),q.end());
	sort(num.begin(),num.end());
	num.erase(unique(num.begin(),num.end()),num.end());
	int l = num.size();
	priority_queue<pii,vector<pii>,cmp> pq;
	ll res = 1ll * n * (n - 1);//所有数对均合法的方案数
	for(int i=0;i<l;i++){
		pq.push({i,0});
	}
	for(int i=0;i<m;i++){
		int x = q[i].first;
		while(!pq.empty() && num[pq.top().first] * num[pq.top().second] < x){//注意判空
			pii tp = pq.top();
			pq.pop();
			res -= cnt[num[tp.first]] * (suf[num[tp.second]] - (tp.second <= tp.first));//注意在拿第二个球的时候第一个球不会放回去
			int npos = tp.second;
			while(npos < l && num[tp.first] * num[npos] < x){
				npos++;
			}
			if(npos < l){
				pq.push({tp.first,npos});
				res += cnt[num[tp.first]] * (suf[num[npos]] - (npos <= tp.first));
			}
		}
		ans[q[i].second] = res;
	}
	for(int i=1;i<=m;i++){
		cout<<ans[i]<<"\n";
	}
	return 0;
}
posted @ 2025-08-21 08:37  yutar  阅读(2)  评论(0)    收藏  举报