线性规划
形式化地说,就是对于一个未定的列向量
x
x
x(长度为
n
n
n),以及一个已知的列向量
c
c
c (长度为
n
n
n),求
c
T
x
c^{T}x
cTx
的最值,以及,对 x x x 的限制是 A x < ( ≤ ) b Ax<(\leq) b Ax<(≤)b ,
其中 A A A 是一个 m × n m\times n m×n 的已知矩阵, b b b 是一个长为 m m m 的已知列向量。
这里的小于符号(其实完全可以换成大于,不重要)表示左边列向量的每一项都分别小于右边列向量的对应项。
线性规划的对偶性
直观的图解法告诉我们:
max
{
c
T
x
∣
A
x
<
b
}
=
min
{
b
T
y
∣
A
T
y
>
c
}
\max\{c^{T}x|Ax<b\}=\min\{b^Ty|A^{T}y>c\}
max{cTx∣Ax<b}=min{bTy∣ATy>c}
左边的线性规划问题和右边的线性规划问题互为对偶问题,二者的答案相等。
实际运用中经常可以遇见这样的情况:把一个线规问题转化成对偶问题后,忽然就可以贪心了。非常好用。
例题
CF671D Roads in Yusland
我们把原问题用线性规划表示出来,令
- c c c :长度为 m m m 的向量,表示各个路径的权值
- A A A : ( n − 1 ) × m (n-1)\times m (n−1)×m 的矩阵, A [ i ] [ j ] A[i][j] A[i][j] 为 1 表示第 i i i 条边在第 j j j 条路径里面。
- b b b :长度为 n − 1 n-1 n−1 ,全为 1 的列向量。
那么我们要求的就是
min
{
c
T
x
∣
A
x
≥
b
}
\min\{c^{T}x|Ax\geq b\}
min{cTx∣Ax≥b}
x x x 本来随意,但是据题意可知最优方案 x x x 一定是个 01 向量。
这本来不那么好贪心,但是我们可以转化成对偶问题看看
min
{
c
T
x
∣
A
x
≥
b
}
=
max
{
b
T
y
∣
A
T
y
≤
c
}
\min\{c^{T}x|Ax\geq b\}=\max\{b^Ty|A^{T}y\leq c\}
min{cTx∣Ax≥b}=max{bTy∣ATy≤c}
右边那个问题就是,给每条边定一个非负整数权值,确保每条路径的边权和不超过路径权值,最大化全体边权和。
这个就可以贪心了,我们从叶子往上确定边权,贪心选取最大边权就行了,用一些数据结构维护路径对每条边的限制,可并堆可以实现一个 log \log log 。
CODE
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#pragma GCC optimize(2)
using namespace std;
#define MAXN 300005
#define MAXM (1<<20|5)
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define PR pair<int,int>
#define UIN unsigned int
int xchar() {
static const int maxn = 1000000;
static char b[maxn];
static int pos = 0,len = 0;
if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
if(pos == len) return -1;
return b[pos ++];
}
// #define getchar() xchar()
inline LL read() {
LL f = 1,x = 0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
inline void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) putchar('-'),x = -x;
return putpos(x);
}
inline void AIput(LL x,int c) {putnum(x);putchar(c);}
int n,m,s,o,k;
int hi[MAXN];
struct it{
int nm,lz;
int s[2],d;
it(){nm=s[0]=s[1]=d=lz=0;}
}tre[MAXN];
int upd(int x) {
if(tre[tre[x].s[0]].d < tre[tre[x].s[1]].d) swap(tre[x].s[0],tre[x].s[1]);
tre[x].d = tre[tre[x].s[1]].d + 1; return x;
}
void pushdown(int x) {
if(tre[x].lz) {
if(tre[x].s[0]) tre[tre[x].s[0]].nm += tre[x].lz,tre[tre[x].s[0]].lz += tre[x].lz;
if(tre[x].s[1]) tre[tre[x].s[1]].nm += tre[x].lz,tre[tre[x].s[1]].lz += tre[x].lz;
tre[x].lz = 0;
} return ;
}
int merg(int a,int b) {
if(!a || !b) return a|b;
if(tre[a].nm > tre[b].nm) swap(a,b);
pushdown(a); tre[a].s[1] = merg(tre[a].s[1],b);
return upd(a);
}
void Pop(int &x) {
pushdown(x);
x=merg(tre[x].s[0],tre[x].s[1]);
}
int hd[MAXN],nx[MAXN<<1],v[MAXN<<1],cne;
void ins(int x,int y) {
nx[++ cne] = hd[x]; v[cne] = y; hd[x] = cne;
}
LL ans = 0;
bool flag = 1;
int d[MAXN],rt[MAXN],ct[MAXN];
void dfs(int x,int ff) {
d[x] = d[ff] + 1;
for(int i = hd[x];i;i = nx[i]) {
if(v[i] != ff) {
dfs(v[i],x);
ct[x] += ct[v[i]];
if(ct[v[i]] <= 0) flag = 0;
while(rt[v[i]] && d[hi[rt[v[i]]]] > d[x]) Pop(rt[v[i]]);
if(rt[v[i]]) {
ans += tre[rt[v[i]]].nm;
tre[rt[v[i]]].lz -= tre[rt[v[i]]].nm;
tre[rt[v[i]]].nm = 0;
}
rt[x] = merg(rt[x],rt[v[i]]);
}
} return ;
}
int main() {
n = read(); m = read();
for(int i = 1;i < n;i ++) {
s = read(); o = read();
ins(s,o); ins(o,s);
}
for(int i = 1;i <= m;i ++) {
s = read(); hi[i] = read();
ct[s] ++; ct[hi[i]] --;
k = read();
tre[i] = it();
tre[i].nm = k;
rt[s] = merg(rt[s],i);
}
dfs(1,0);
if(!flag) ans = -1;
AIput(ans,'\n');
return 0;
}