s_r_f
2020-02-28 19:05:50
为什么写这篇
拉格朗日乘数法在今天训练的一道题上用到了
但我却只会暴力
但是赛后一看他们的
去网上搜了一下这种做法
所以今天来补一补数学知识
拉格朗日乘数法
求一个多元函数
可以转化为
无条件极值的求法
对
至于正确性
正确性
具体的证明我不会
这里有两个链接以供参考
实际上这种方法只能求出极值
理论的内容就这么多吧
因为我不太会打
批注:OI里的函数基本上都很平滑,所以乱搞也没关系的(
求反比例函数
即最小化
为了简化问题
这样问题要最小化的东西还是没变
对
对
对
最后解得
给你
求这些点组成的点集中
枚举凸包上的点集和点的排列方式
现在我们令凸包上的点数为
令
由于三角形的面积
答案即目标函数
限制条件
对
对
这个并不能让我们直接解出所有的
注意到
所以我们可以二分
进而求出
那么这道题就做完了
时间复杂度
留作课后练习
还是推式子
先留个坑
对
然后考虑二分
不过不合法的部分排序后显然是连续的一段
然后求得了实数解
#include <bits/stdc++.h>
#define LL long long
#define LD long double
using namespace std;
template <typename T> void read(T &x){
static char ch; x = 0,ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
}
inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
const int N = 100005;
LL n,k;
LL id[N],a[N]; LD b[N];
LL ans[N];
struct node{ int id; LL v; inline bool operator < (const node w) const{ return v < w.v; } }c[N];
int cnto;
inline int check(int p){
LD L = -1e20,R = 1e20,Mid,kk = k,tot; int i; bool ok = 0;
for (i = 1; i <= p; ++i) kk -= a[i];
for (i = p+1; i <= n; ++i) L = max(L,(LD)a[i] - 3 * a[i] * a[i]),R = min(R,(LD)a[i]);
while (R-L>1){
Mid = (L+R)/2;
tot = 0;
for (i = p+1; i <= n; ++i) b[i] = sqrt((a[i]-Mid)/3),tot += b[i];
if (tot >= kk) ok = 1,L = Mid; else R = Mid;
}
for (i = p+1; i <= n; ++i) b[i] = sqrt((a[i]-L)/3);
return ok;
}
int main(){
int i;
read(n),read(k);
for (i = 1; i <= n; ++i) read(c[i].v),c[i].id = i;
sort(c+1,c+n+1);
for (i = 1; i <= n; ++i) id[i] = c[i].id,a[i] = c[i].v;
int L = 0,R = n,Mid,Ans = 0;
while (L <= R){
Mid = L+R>>1;
for (i = 1; i <= Mid; ++i) b[i] = a[i];
if (check(Mid)) Ans = Mid,R = Mid - 1;
else L = Mid + 1;
}
for (i = 1; i <= Ans; ++i) b[i] = a[i];
check(Ans);
for (i = 1; i <= n; ++i) b[i] = floor(b[i]),k -= b[i];
for (i = 1; i <= n; ++i) if (b[i] < a[i]){
++cnto;
c[cnto].id = i,c[cnto].v = a[i] - 3 * b[i] * b[i] - 3 * b[i];
}
sort(c+1,c+cnto+1);
reverse(c+1,c+cnto+1);
for (i = 1; i <= k; ++i) b[c[i].id] += 1;
for (i = 1; i <= n; ++i) ans[id[i]] = b[i];
for (i = 1; i <= n; ++i) write(ans[i]),putchar(i<n?' ':'\n');
return 0;
}