题解 钱仓
一个比较显然的思路是断环为链后枚举断点,然后堆贪心check
然后考虑怎么剪枝,应该是去找断点的性质
比较显然的是断点前面仓库的钱不会运到这个仓库及以后
另一个性质是从这个仓库向下不断运的话中间不会有空的仓库
- 一个环,有些节点上有东西,最后要通过运送使所有节点上的物品数量相等:
先求出最后每个节点上的物品数量,于是每个节点可以被表示成一个可负整数
关于最小运输总量:断环为链后枚举断点,然后堆贪心check
发现从一个不会从这个点前面向后面运物品的点是一个可以取到最优解的断点
那就有个性质,最优解的断点向下运的过程中不会出现一个点不够被前面的多余物品补到基准线
这个看起来不太好判断的样子,但其实可以翻译成「与基准线作差后前缀和中没有小于起点前缀和的点」,这样就好check了 - 关于最大子段和:可以单调队列求出,用线段树可以 \(O(logn)\) 查询任意区间
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long
#define fir first
#define sec second
#define make make_pair
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
int c[N];
namespace force{
ll ans=INF;
priority_queue<ll> q1, q2;
void solve() {
for (int i=1; i<=n; ++i) c[n+i]=c[i];
for (int i=1; i<=n; ++i) if (c[i]) {
// cout<<"i: "<<i<<endl;
while (q1.size()) q1.pop();
ll sum=0;
for (int j=i; j<=i+n-1; ++j) {
for (int k=1; k<=c[j]; ++k) q1.push(0);
if (q1.size()) {sum+=q1.top()*q1.top(); q1.pop();}
while (q1.size()) q2.push(q1.top()+1), q1.pop();
swap(q1, q2);
}
if (!q1.size()) ans=min(ans, sum);
}
printf("%lld\n", ans);
exit(0);
}
}
namespace task1{
ll ans=INF;
priority_queue<ll> q;
void solve() {
for (int i=1; i<=n; ++i) c[n+i]=c[i];
for (int i=1; i<=n; ++i) if (c[i]) {
// cout<<"i: "<<i<<endl;
while (q.size()) q.pop();
ll sum=0; int dlt=0;
for (int j=i; j<=i+n-1; ++j) {
for (int k=1; k<=c[j]; ++k) q.push(-dlt);
if (q.size()) {sum+=(q.top()+dlt)*(q.top()+dlt); q.pop();}
else goto jump;
++dlt;
}
if (!q.size()) ans=min(ans, sum);
jump: ;
}
printf("%lld\n", ans);
exit(0);
}
}
namespace task{
ll ans=INF;
priority_queue<ll> q;
int sum[N], tl[N<<2], tr[N<<2], minn[N<<2];
#define tl(p) tl[p]
#define tr(p) tr[p]
#define minn(p) minn[p]
#define pushup(p) minn(p)=min(minn(p<<1), minn(p<<1|1))
void build(int p, int l, int r) {
tl(p)=l; tr(p)=r;
if (l==r) {minn(p)=sum[l]; return ;}
int mid=(tl(p)+tr(p))>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
pushup(p);
}
int query(int p, int l, int r) {
if (l<=tl(p)&&r>=tr(p)) return minn(p);
int mid=(tl(p)+tr(p))>>1, ans=INF;
if (l<=mid) ans=min(ans, query(p<<1, l, r));
if (r>mid) ans=min(ans, query(p<<1|1, l, r));
return ans;
}
void solve() {
for (int i=1; i<=n; ++i) c[n+i]=c[i];
for (int i=1; i<=n; ++i) sum[i]=sum[i-1]+c[i]-1;
build(1, 1, n<<1);
for (int i=1; i<=n; ++i) if (c[i] && query(1, i, i+n-1)>=sum[i-1]) {
// cout<<"i: "<<i<<endl;
while (q.size()) q.pop();
ll ans=0; int dlt=0;
for (int j=i; j<=i+n-1; ++j) {
for (int k=1; k<=c[j]; ++k) q.push(-dlt);
if (q.size()) {ans+=(q.top()+dlt)*(q.top()+dlt); q.pop();}
else puts("error");
++dlt;
}
printf("%lld\n", ans);
exit(0);
}
puts("error");
}
}
signed main()
{
freopen("barn.in", "r", stdin);
freopen("barn.out", "w", stdout);
n=read();
for (int i=1; i<=n; ++i) c[i]=read();
// force::solve();
task::solve();
return 0;
}