# | 제출 시각 | 아이디 | 문제 | 언어 | 결과 | 실행 시간 | 메모리 |
---|---|---|---|---|---|---|---|
406666 | drogskol | Aliens (IOI16_aliens) | C++17 | 0 ms | 0 KiB |
이 제출은 이전 버전의 oj.uz에서 채점하였습니다. 현재는 제출 당시와는 다른 서버에서 채점을 하기 때문에, 다시 제출하면 결과가 달라질 수도 있습니다.
#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);i++)
#define ALL(v) v.begin(),v.end()
//base:[Convex-Hull Trick - sataniC++](http://s...content-available-to-author-only...g.com/entry/2016/08/16/181331#fn-534591c8)
//追加する直線の傾きは単調(最小値なら傾きは単調減少、最大値なら傾きは単調増加)
template<typename T>
struct ConvecHullTrick {
typedef pair<T,T> Line;
vector<Line> lines; //直線(傾き,切片)
bool isMonotonicX; //最小値(最大値)を求めるxが単調であるか
function<bool(T l, T r)> comp; //最小/最大を判断する関数
//flag:クエリは単調? compFunc:求めるのは最大値?
ConvecHullTrick(bool flagX=0,bool compFunc=0):isMonotonicX(flagX){
if(compFunc)comp=[](T l,T r){return l<=r;};
else comp=[](T l,T r){return l>=r;};
};
//直線l1,l2,l3のうちl2が不必要であるかどうか
bool check(Line l1,Line l2,Line l3){
if(l1<l3)swap(l1,l3);
T a1=l1.first,a2=l2.first,a3=l3.first,b1=l1.second,b2=l2.second,b3=l3.second;
return (b3-b2)*(a2-a1)>=(b2-b1)*(a3-a2);
}
//直線y=ax+bを追加する 単調性から付け足すのは一番後ろで、不必要なやつが消えるまで一番後ろを消したら付け足す
void add(T a, T b) {
Line line(a,b);
while(lines.size()>=2&&check(*(lines.end()-2),lines.back(),line))lines.pop_back();
lines.emplace_back(line);
}
//linesのi番目の直線のxでの値
T f(int i,T x) {
return lines[i].first*x+lines[i].second;
}
//特定のlineのxでの値
T f(Line line,T x) {
return line.first*x+line.second;
}
//直線群の中でxの時に最小(最大)となる値を返す
T get(T x){
assert(lines.size());
if(isMonotonicX){//最小値(最大値)クエリにおけるxが単調(昇順)
int head=0;
while(lines.size()-head>=2&&comp(f(head,x),f(head+1,x)))head++;
return f(head,x);
}
else{
int low=-1,high=lines.size()-1;
while(high-low>1){
int mid=(high+low)>>1;
if(comp(f(mid,x),f(mid+1,x)))low=mid;
else high=mid;
}
return f(high,x);
}
}
};
using ll=long long;
typedef pair<int,int> P;
using ld=long double;
static const ld GRATIO = 1.6180339887498948482045868343656;
ll square(int a){
return a*(ll)a;
}
ll shortest(int n,int k,const vector<P>&v,ll t){//t:ラグランジュ双対のパラメータ
ConvecHullTrick<ll> CHT(1,0);
REP(i,n){
//[0,i)の最小コストをDとする
//[i,j]を囲うような正方形を考えると、これの大きさは(v[j].se-v[i].fi)^2 = v[j].se^2 -2v[j].se v[i]fi + v[i].se^2
//また、v[i-1].se>v[i].fiの時はi-1までの正方形との重複が(v[i-1].se-v[i].fi)^2
//ここにラグランジュ変数のtを足す必要がある
//したがって、(-2v[i].fi)x + t+D+v[i].se^2 -(v[i-1].se-v[i].fi)^2 を付けてあげるようにすると、jまでの最小値がget(v[j].se)+v[j].se^2になる
//v[i].fiは単調増加なので傾きは単調減少 v[i].seも単調増加にしてるのでクエリも単調増加
ll D=( i ? CHT.get(v[i-1].second)+square(v[i-1].second) : 0LL );//[0,i)を撮る最小コスト
if(i&&v[i-1].second>v[i].first)D-=square(v[i-1].second-v[i].first);
ll a=-2*v[i].first,b=square(v[i].first)+t+D;
CHT.add(a,b);
}
ll D=CHT.get(v[n-1].second)+square(v[n-1].second);
return D-t*k;
}
ll take_photos(int n,int m,int k,vector<int> r,vector<int> c){
vector<P> v,tmp(n);
REP(i,n)tmp[i]={min(r[i],c[i])-1,max(r[i],c[i])};
sort(ALL(tmp));
for(P p:tmp){
if(v.size()&&v.back().second>=p.second)continue;
if(v.size()&&v.back().first==p.first)v.pop_back();
v.push_back(p);
}
n=v.size();
REP(i,n-1){
assert(v[i].first<v[i+1].first);
assert(v[i].second<v[i+1].second);
}
if(n<=k){
ll res=0;
//cout<<"N"<<n<<endl;
//for(P p:v)cout<<p.first<<" "<<p.second<<endl;
for(int i=0;i<n;i++){
res+=square(v[i].second-v[i].first);
if(i&&v[i-1].second>v[i].first)res-=square(v[i-1].second-v[i].first);
}
return res;
}
ll lt=-3e12,rt=3e12;
ll t1=lt+floor((ld)(rt-lt)/(1.0+GRATIO)),t2=lt+ceil((ld)(rt-lt)*GRATIO/(1.0+GRATIO));
ll va1=shortest(n,k,v,t1),va2=shortest(n,k,v,t2);
while(rt-lt>2){
if(va1<va2){
lt=t1;
t1=t2;
va1=va2;
t2=lt+ceil((ld)(rt-lt)*GRATIO/(1.0+GRATIO));
va2=shortest(n,k,v,t2);
}
else{
rt=t2;
t2=t1;
va2=va1;
t1=lt+floor((ld)(rt-lt)/(1.0+GRATIO))
va1=shortest(n,k,v,t1);
}
}
//cout<<"T"<<(lt+rt)/2<<endl;
return shortest(n,k,v,(lt+rt)/2);
}
/*
int main(){
int n,m,k;cin>>n>>m>>k;
vector<int> r(n),c(n);
REP(i,n)cin>>r[i]>>c[i];
cout<<take_photos(n,m,k,r,c)<<endl;
}
*/