NOIP2023模拟9联测30 T3 高爸
三分啊,三分……
思路
设现在的平均力量值为 \(x\),大于 \(x\) 力量值的龙有 \(n\) 条,小于等于的龙有 \(m\) 条,花费为:
\[a(n \times x-\sum_{i=1}^{n+m} p_i (p_i>x))+b(\sum_{i=1}^{n+m} p_i (p_i \leq x) - m \times x) \]对于 \(a(n \times x-\sum_{i=1}^{n+m} p_i (p_i>x))\) 和 \(b(\sum_{i=1}^{n+m} p_i (p_i \leq x) - m \times x)\) 来说,都具有三分性质,凹函数的和也具有三分性质,所以可以三分。
这里写的是关于 \(x\) 值域的三分。
将 \(p_i\) 离散化后,钦定 \(x\) 后求答案时,使用树状数组求 \(p_i\) 的前缀和和小于 \(x\) 的个数。
三分时间复杂度 \(\log n\),树状数组时间复杂度 \(\log n\),总复杂度 \(O(n \log^2 n)\)。
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn=1e5+5;
int n,cnt;
int nx[maxn],tmp[maxn],fp[maxn],x[maxn];
ll a,b,sum;
ll ts[maxn],tc[maxn];
unordered_map<ll,int>mp;
set< ll >s;
inline ll read() {
ll s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
inline void write(ll X)
{
if(X<0) {X=~(X-1); putchar('-');}
if(X>9) write(X/10);
putchar(X%10+'0');
}
inline void lsh()
{
copy(x+1,x+n+1,tmp+1);
sort(tmp+1,tmp+n+1);
tmp[0]=-1;
for(ll i=1;i<=n;i++)
if(tmp[i]!=tmp[i-1]) mp[tmp[i]]=++cnt,fp[cnt]=tmp[i];
for(ll i=1;i<=n;i++) nx[i]=mp[x[i]];
}
ll lowbit(ll x){return x&(-x);}
inline void updata(int x,ll y)
{
while(x<=n)
{
ts[x]+=y;
tc[x]++;
x+=lowbit(x);
}
}
inline pair<ll,ll> gtsum(int x)
{
ll sumcnt=0,sumsum=0;
while(x)
{
sumsum+=ts[x];
sumcnt+=tc[x];
x-=lowbit(x);
}
return make_pair(sumcnt,sumsum);
}
inline ll gt(ll x,ll t)
{
auto k=s.upper_bound(x);
if(k==s.begin()) return (sum-t*x)*b;
k--;
ll tp=0;
tp=mp[*k];
pair<ll,ll> tmp=gtsum(tp);
ll tcnt=tmp.first,tsum=tmp.second;
return (tcnt*x- tsum)*a+(sum-tsum-(t-tcnt)*x)*b;
}
inline ll gtans(int t)
{
int l=1,r=1e9;
ll ans=2e18;
while(l<=r)
{
int mid=l+r>>1;
int midl=mid+l>>1,midr=mid+r>>1;
ll vm=gt(mid,t),vml=gt(midl,t),vmr=gt(midr,t);
if(vml<=vm&&vm<=vmr) ans=vml,r=midr-1;
else if(vml>=vm&&vm>=vmr) ans=vmr,l=midl+1;
else ans=vm,l=midl+1,r=midr+1;
}
return ans;
}
signed main()
{
scanf("%d%lld%lld",&n,&a,&b);
for(int i=1;i<=n;i++) x[i]=read();
lsh();
printf("0\n");
updata(nx[1],x[1]);
sum=x[1];
s.insert(x[1]);
for(int i=2;i<=n;i++)
{
sum+=x[i];
updata(nx[i],x[i]);
s.insert(x[i]);
ll ans=gtans(i);
write(ans);
putchar('\n');
}
}
后记
常数有点小大,需要快读快输和 inline。
标签:tmp,ch,int,ll,30,T3,高爸,maxn,sum From: https://www.cnblogs.com/binbinbjl/p/17806513.html