題意
營地裡的人一睡著,基里爾就偷偷溜出帳篷,去智者橡樹那裡採蘑菇。
眾所周知,橡樹下生長著 \(n\) 種蘑菇,每種蘑菇都有 \(v_i\) 的魔力。基里爾非常想用這些蘑菇製作一種魔力最大的靈藥。
靈藥的強度等於其中蘑菇的數量與這些蘑菇中最小魔力的乘積。為了配製靈藥,基里爾將依次採摘生長在橡樹下的蘑菇。基里爾可以按照任何順序採集蘑菇。
然而,事情並非如此簡單。智慧橡樹告訴基里爾從 \(1\) 到 \(n\) 的數字 \(p\) 的排列組合。如果基里爾只採摘 \(k\) 蘑菇,那麼所有指數為 \(p_1, p_2, \dots, p_{k - 1}\) 的蘑菇的魔力就會變成 \(0\) 。基里爾不會使用魔力為零的蘑菇來配製靈藥。
你的任務是幫助基里爾採集蘑菇,使他能夠釀造出最大魔力的靈藥。不過,基里爾有點害怕在橡樹附近逗留太久,所以在所有適合採集蘑菇的選項中,他要求您找到蘑菇數量最少的那個。
長度為 \(n\) 的排列是由 \(n\) 個不同的整陣列成的陣列,這些整數的順序從 \(1\) 到 \(n\) 。例如, \([2,3,1,5,4]\) 是一個排列,但 \([1,2,2]\) 不是排列( \(2\) 在陣列中出現兩次), \([1,3,4]\) 也不是排列( \(n=3\) ,但 \(4\) 在陣列中出現)。
思路
考慮列舉摘 \(k\) 個蘑菇,然後就可以獲得哪些蘑菇會歸 \(0\) , 則答案貪心的想即選前 \(k\) 個蘑菇
則最小值為當前序列第 \(k\) 大
支援修改且可以動態維護第 \(k\) 大的顯然是權值線段樹
程式碼
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
int a[N];
int b[N];
int v[N];
struct node
{
int l,r;
int sum;
}tr[N<<2];
void pushup(int id){tr[id].sum=tr[id<<1].sum+tr[id<<1|1].sum;}
void build(int id,int l,int r)
{
tr[id]={l,r};
if(l==r)
{
return;
}
int mid=(l+r)/2;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
}
void change(int id,int x,int v)
{
if(tr[id].l==tr[id].r)
{
tr[id].sum+=v;
return;
}
int mid=(tr[id].l+tr[id].r)/2;
if(x<=mid) change(id<<1,x,v);
else change(id<<1|1,x,v);
pushup(id);
}
int find(int id,int k)
{
if(tr[id].l==tr[id].r)
{
return tr[id].l;
}
if(k<=tr[id<<1|1].sum) return find(id<<1|1,k);
else return find(id<<1,k-tr[id<<1|1].sum);
}
int main()
{
int _;
cin>>_;
while(_--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+1+n);
int m=unique(b+1,b+1+n)-b-1;
build(1,1,m);
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b+1,b+1+m,a[i])-b;
change(1,a[i],1);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
}
long long ans=b[m];
int id=1;
for(int i=2;i<=n;i++)
{
if(n-i+1<i) break;
change(1,a[v[i-1]],-1);
long long cnt=1LL*b[find(1,i)]*i;
if(cnt>ans)
{
ans=cnt;
id=i;
}
}
cout<<ans<<" "<<id<<"\n";
}
}