题意:
给你n个数,cnt[i]表示i出现的次数。
求出最大的(x+y)*(cnt[x]+cnt[y])
x不等于y并且给你一些不能取的对数。
题解:
一开始想的是那种随着值增大出现次数单调递减的二分。但是这种不能取的情况一旦出现就导致可能不是最优的,那么二分怎么做我一下子就想不出来了。
值想不出来换个思路,通过出现次数枚举,我们会发现不同种类的出现次数最多只有
O
(
n
)
O(\sqrt{n})
O(n
)次。
最坏情况应该是这样:出现1次的有1个,2次的有1个,3次的有1个…那情况数为(1+x)*x/2<=n。
然后我们由于要使乘积最大,对于相同出现次数的我们只需要找最大的就好了。但是这里有一个问题就是说有不能取的对,所以对于相同出现次数的,我们还是要值从高往低找到能取的值。由于这个时间复杂度是独立的,所以总时间复杂度就是O(NlogN)。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3e5+5;
const ll M=1e9+7;
struct node{
ll val,num;
}a[N];
#define pll pair<ll,ll>
struct pair_hash{
template<class T1,class T2>
std::size_t operator()(const std::pair<T1,T2>&p)const {
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);
return h1*M+h2;
}
};
unordered_map<pll,ll,pair_hash>mp;
unordered_map<ll,ll>num;
ll check(ll x,ll i){
return (a[x].val+i)*(a[x].num+num[i]);
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
num.clear();
mp.clear();
int n,k;
ll x,y;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%lld",&x),num[x]++;
for(int i=1;i<=k;i++)
scanf("%lld%lld",&x,&y),mp[{x,y}]=mp[{y,x}]=1;
ll all=0,mx=0;
for(auto i :num){
int l=1,r=all,ans=0,mid;
if(all){
while(r>=l){
mid=l+r>>1;
if(!ans)ans=mid;
if(check(mid,i.first)>check(ans,i.first))
ans=mid;
if(check(mid,i.first)>=check(mid+1,i.first))r=mid-1;
else l=mid+1;
}
if(mp[{i.first,a[ans].val}]){
l=r=ans;
while(l&&mp[{i.first,a[l].val}])l--;
while(r<=all&&mp[{i.first,a[r].val}])r++;
if(l)mx=max(mx,check(l,i.first));
if(r-all-1)mx=max(mx,check(r,i.first));
}
else
mx=max(mx,check(ans,i.first));
}
while(all&&i.second>=a[all].num)all--;
a[++all]={i.first,i.second};
}
printf("%lld\n",mx);
}
return 0;
}