题解:P12545 [UOI 2025] Partitioning into Three
Solve
看到环直接断环为链,因为题目要求三段和于是顺手求前缀和,记
显然三段和极差最小时,每段的和会尽量接近
对每个每个候选的
计算这至多
最后输出三个切点时,只需把在链上的
正确性感性理解就行了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,a[N],b[N*2],mx=-1,mn=1e9+1;
ll sum,sumb[N*2],ans=LLONG_MAX;
int ansx,ansy,ansz,p[4];
ll query(int l,int r){
return sumb[r]-sumb[l-1];
}
void check(int x,int y,int z){
if(!(x+1<=y && y<=x+n-2)) return;
if(!(y<=z && z<=x+n-2)) return;
ll s1=query(x,y-1);
ll s2=query(y,z);
ll s3=sum-s1-s2;
ll mx=max({s1,s2,s3});
ll mn=min({s1,s2,s3});
ll diff=mx-mn;
if(diff<ans){
ans=diff;
ansx=x;
ansy=y;
ansz=z+1;
}
}
int find(int t){
int r=t%n;
if(r==0) r=n;
return r;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
freopen("circle.in","r",stdin);
freopen("circle.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
b[i]=a[i];
b[i+n]=a[i];
sum+=a[i];
mx=max(mx,a[i]);
mn=min(mn,a[i]);
}
if(n==3){
cout<<mx-mn<<"\n";
cout<<1<<" "<<2<<" "<<3;
return 0;
}
for(int i=1;i<=n*2;++i)
sumb[i]=sumb[i-1]+b[i];
int j=0;
for(int x=1;x<=n;++x){
while(j<x+n-1){//指针j找切点y的边界
ll s1=query(x,j-1);
if(s1*3<sum) ++j;
else break;
}
vector<int> v;//候选y集合
int j1=j-1;
int j2=min(j,x+n-2);//y不能到x+n-1(否则第二段空)
if(j1>=x+1) v.push_back(j1);
if(j2>=x+1){
if(v.empty() || j2!=v.back())
v.push_back(j2);
}
for(int y : v){
ll s1=query(x,y-1);
ll rem=sum-s1;
ll t=sumb[y-1]+rem/2;
//在[y...x+n-2]上找z
int L=y,R=x+n-1;
int pos=lower_bound(sumb+L,sumb+R,t)-sumb;
//候选z:pos与pos-1,均需∈[y...x+n-2]
if(pos<x+n-1) check(x,y,pos);
if(pos-1>=y) check(x,y,pos-1);
}
}
p[1]=find(ansx),p[2]=find(ansy),p[3]=find(ansz);
sort(p+1,p+1+3);
cout<<ans<<"\n";
cout<<p[1]<<" "<<p[2]<<" "<<p[3];
return 0;
}
枚举