Skip to content

Commit

Permalink
3.14
Browse files Browse the repository at this point in the history
  • Loading branch information
dtcxzyw committed Mar 14, 2019
1 parent 8bdb247 commit c0f6e06
Show file tree
Hide file tree
Showing 12 changed files with 451 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"-std=c++11",
"-D_FORTIFY_SOURCE=2",
"-fstack-protector-all",
"-ftrapv",
//"-ftrapv",
"-ggdb3",
"-Wextra"
],
Expand Down
10 changes: 5 additions & 5 deletions Queue.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,12 @@
- [ ] [NOI2014]购票
- [ ] [NOI2016]国王饮水记
- [ ] BZOJ1096 [ZJOI2007]仓库建设
# 凸优化(WQS二分、带权二分)
- [ ] CF 739E
- [ ] [国家集训队2]Tree I
- [ ] [SDOI2016]征途
- [ ] [八省联考2018]林克卡特树lct
- [ ] BZOJ5311贞鱼
# 凸优化(WQS二分、带权二分)
- [x] CF 739E
- [x] [国家集训队2]Tree I
- [x] [八省联考2018]林克卡特树lct
- [x] BZOJ5311贞鱼
# 单调队列
- [x] P2254 [NOI2005]瑰丽华尔兹
- [x] P3572 [POI2014]PTA-Little Bird
Expand Down
15 changes: 13 additions & 2 deletions Review/DP/Convex.tex
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,23 @@ \section{WQS二分凸优化}
$f'(x)=g'(x)+k$的根移动到$C$。此时对应的代价函数$f(x)=g(x)+kx$,最后的答案
$g(C)=f(C)-kC$

在二分的过程中,先做一遍常规DP,考虑$f(x)=g(x)+kx$,其实际意义为每多选一个物品需要额外
增加$k$的权值,再做DP就可以求出$f(x)$的极值点。
二分需要求出$f(x)$的极值点,$k$的实际意义是每多选一个物品需要额外增加$k$的权值,引入$k$
后忽略了个数限制,可以做更低维的DP/贪心。这种优化方法不仅在DP中使用,比如[国家集训队2]
Tree I。

注意这里只需要整数二分。由于DP的特殊性,$g(x)$的值域为整数,其图像由许多横向长度为1的线段
组成。那么$g'(x)$的函数图像就是许多取值为整数的水平线,因此只需二分整数偏移。

{\bfseries BZOJ5311 贞鱼:注意可能存在连续极值点的情况,此时要求极左或极右的极值点,
然后根据极值点是极左还是极右判断二分中更新答案的位置。在本题中求的是极左极值点,因此在
极左极值点$\leq$目标点时更新答案。如果在极左极值点$\geq$目标点时更新答案,则无法取到极左
极值点到极右极值点覆盖目标点的情况。}

{\bfseries CF739E Gosha is hunting:当有二个个数约束时使用二分套二分解决。虽然使用
浮点二分,但仍然要注意极值点取极左/极右,控制不等号来控制区间缩小方向。}

优化:如果发现极值点恰好是目标点,直接break。

上述内容参考了FlashHu的博客\footnote{
DP的各种优化(动态规划,决策单调性,斜率优化,带权二分,单调栈,单调队列)\\
\url{https://www.cnblogs.com/flashhu/p/9480669.html}
Expand Down
25 changes: 25 additions & 0 deletions Review/Other/Owys.tex
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,28 @@ \subsection{读入优化}
可以预先取得它们的值。}

结果很明显,显式fread/fwrite速度最快。
\subsection{快速乘法取模}
当模数的平方超过long long的表示范围时,可以使用类似快速幂的方式计算快速乘法。

还有一个无法严格证明正确性的trick:将$a*b\%mod$表示为$a*b-\lfloor a/mod*b\rfloor*mod$
其中$floor$内部使用long double计算。$floor$操作直接使用强制转型,因为$a/mod*b$就在
long long的表示范围内。由于最终结果在范围内,两个乘法事实上计算的是模$2^64$意义下的乘法,
暂时溢出并没有关系。

实现代码:
\begin{lstlisting}
typedef long long Int64;
Int64 mulmB(Int64 a, Int64 b) {
Int64 res =
(a * b -
static_cast<Int64>(
static_cast<long double>(a) / mod * b) *
mod) %
mod;
return res < 0 ? res + mod : res;
}
\end{lstlisting}

无法证明正确性的原因在于long double的精度是否足够,而且在MSVC中long double只有64位
精度而不是80位。不过让人放心的是无论是long double还是double,在$\geq 10^8$次随机测试
中没有出现错误。
3 changes: 2 additions & 1 deletion Review/Other/TricksAndIdeas.tex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ \subsection{二分/三分}

若目标函数为凸(尤其是一些计算几何题),一般使用三分法判断函数的``轮廓''
以达到缩小搜索范围的目的。使用斐波那契法(或者0.618法)在区间$[0,1]$选取点
$\frac{f_{n-2}}{f_n},\frac{f_{n-1}}{f_n}$作为比较点或许会更玄学。
$\frac{f_{n-2}}{f_n},\frac{f_{n-1}}{f_n}$作为比较点或许会更玄学。对于整数
三分,在范围缩小到6以内时暴力枚举。
\subsection{补集转化}
根据``正难则反''的哲学,若正向思考不好解决,则考虑它的反面。尤其在
计数问题中考虑所求计数集合的补集。此外在并查集中引入补集的概念也是
Expand Down
2 changes: 2 additions & 0 deletions Review/Tree/Diameter.tex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ \subsection{定义与计算}
\item$b$点开始DFS计算与其相距最远的点$c$
\item $b-c$就是树的直径。
\end{enumerate}

{\bfseries 注意此法不能用于带负权直径。}
\end{itemize}

\subsection{性质}
Expand Down
42 changes: 42 additions & 0 deletions Source/DP/bzoj5311TLE.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <algorithm>
#include <cstdio>
#include <cstring>
const int size = 4005;
int A[size][size], B[size][size], dp[2][size], *F, *G;
void solve(int l, int r, int b, int e) {
if(l > r)
return;
int m = (l + r) >> 1, end = std::min(e, m - 1);
int dpv = 1 << 30, tp;
for(int i = b; i <= end; ++i) {
int cv = F[i] + B[i + 1][m];
if(cv < dpv)
dpv = cv, tp = i;
}
G[m] = dpv;
solve(l, m - 1, b, tp);
solve(m + 1, r, tp, e);
}
int main() {
int n, k;
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j) {
scanf("%d", &A[i][j]);
A[i][j] += A[i][j - 1];
}
for(int i = 1; i <= n; ++i) {
for(int j = i; j <= n; ++j)
B[i][j] =
B[i][j - 1] + A[j][j] - A[j][i - 1];
}
memset(dp, 0x3f, sizeof(dp));
F = dp[0], G = dp[1];
G[0] = 0;
for(int i = 1; i <= k; ++i) {
std::swap(F, G);
solve(1, n, 0, n);
}
printf("%d\n", G[n]);
return 0;
}
64 changes: 64 additions & 0 deletions Source/WQS/2619.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <algorithm>
#include <cstdio>
#include <cstring>
int read() {
int res = 0, c;
do
c = getchar();
while(c < '0' || c > '9');
while('0' <= c && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
return res;
}
const int size = 50005;
struct Edge {
int u, v, w, c, rw;
bool operator<(const Edge& rhs) const {
return rw != rhs.rw ? rw < rhs.rw : c < rhs.c;
}
} E[2 * size];
int fa[size];
int find(int x) {
return fa[x] != -1 ? fa[x] = find(fa[x]) : x;
}
std::pair<int, int> solve(int n, int m, int k) {
memset(fa, -1, sizeof(int) * n);
for(int i = 0; i < m; ++i)
E[i].rw = E[i].w + (E[i].c ? 0 : k);
std::sort(E, E + m);
int sumw = 0, wcnt = 0;
for(int i = 0; i < m; ++i) {
int u = find(E[i].u), v = find(E[i].v);
if(u != v) {
sumw += E[i].rw;
wcnt += (E[i].c == 0);
fa[u] = v;
}
}
return std::make_pair(wcnt, sumw);
}
int main() {
int n = read();
int m = read();
int c = read();
for(int i = 0; i < m; ++i) {
E[i].u = read();
E[i].v = read();
E[i].w = read();
E[i].c = read();
}
const int range = 100;
int l = -range, r = range, ans;
while(l <= r) {
int mid = (l + r) >> 1;
std::pair<int, int> res = solve(n, m, mid);
if(res.first >= c)
l = mid + 1, ans = res.second - mid * c;
else
r = mid - 1;
}
printf("%d\n", ans);
return 0;
}
54 changes: 54 additions & 0 deletions Source/WQS/CF739E.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include <algorithm>
#include <cstdio>
typedef double FT;
const FT eps = 1e-8;
const int size = 2005;
FT A[size], B[size], C[size];
struct Res {
FT fv;
int a, b;
Res(FT fv, int a, int b) : fv(fv), a(a), b(b) {}
};
void CAS(FT& fv, int& da, int& db, FT cv, int a,
int b) {
if(cv > fv)
fv = cv, da = a, db = b;
}
Res solve(int n, FT ka, FT kb) {
FT kc = ka + kb, cfv = 0.0;
int ca = 0, cb = 0;
for(int i = 1; i <= n; ++i) {
FT nfv = 0.0;
int da = 0, db = 0;
CAS(nfv, da, db, A[i] - ka, 1, 0);
CAS(nfv, da, db, B[i] - kb, 0, 1);
CAS(nfv, da, db, C[i] - kc, 1, 1);
ca += da, cb += db, cfv += nfv;
}
return Res(cfv, ca, cb);
}
int main() {
int n, a, b;
scanf("%d%d%d", &n, &a, &b);
for(int i = 1; i <= n; ++i)
scanf("%lf", &A[i]);
for(int i = 1; i <= n; ++i)
scanf("%lf", &B[i]);
for(int i = 1; i <= n; ++i)
C[i] = A[i] + B[i] - A[i] * B[i];
FT la = 0.0, ra = 1.0, ans;
while(ra - la > eps) {
FT ma = (la + ra) / 2.0;
FT lb = 0.0, rb = 1.0;
while(rb - lb > eps) {
FT mb = (lb + rb) / 2.0;
Res res = solve(n, ma, mb);
(res.b >= b ? lb : rb) = mb;
}
Res res = solve(n, ma, lb);
(res.a >= a ? la : ra) = ma;
ans = res.fv + a * ma + b * lb;
}
printf("%.8lf\n", ans);
return 0;
}
117 changes: 117 additions & 0 deletions Source/WQS/LOJ2478.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include <algorithm>
#include <cstdio>
#include <cstring>
namespace IO {
const int size = 1 << 23;
char in[size], *S = in;
void init() {
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
fread(in, 1, size, stdin);
}
char getc() {
return *S++;
}
}
int read() {
int res = 0, c;
bool flag = false;
do {
c = IO::getc();
flag |= c == '-';
} while(c < '0' || c > '9');
while('0' <= c && c <= '9') {
res = res * 10 + c - '0';
c = IO::getc();
}
return flag ? -res : res;
}
int iabs(int x) {
return x < 0 ? -x : x;
}
const int size = 300005;
struct Edge {
int to, nxt, w;
} E[2 * size];
int last[size], cnt = 0;
void addEdge(int u, int v, int w) {
++cnt;
E[cnt].to = v, E[cnt].nxt = last[u], E[cnt].w = w;
last[u] = cnt;
}
typedef long long Int64;
#define asInt64 static_cast<Int64>
Int64 C;
struct Chain {
Int64 sum;
int cnt;
Chain() : sum(0), cnt(0) {}
Chain(Int64 sum, int cnt) : sum(sum), cnt(cnt) {}
bool operator<(const Chain& rhs) const {
return sum == rhs.sum ? cnt > rhs.cnt :
sum < rhs.sum;
}
Chain operator+(const Chain& rhs) const {
return Chain(sum + rhs.sum, cnt + rhs.cnt);
}
Chain operator+(int len) const {
return Chain(sum + len, cnt);
}
};
Chain makeChain(const Chain& c) {
return Chain(c.sum + C, c.cnt + 1);
}
struct Info {
Chain L0, L1;
Info(const Chain& L0, const Chain& L1)
: L0(L0), L1(L1) {}
};
Info DFS(int u, int p) {
Chain L0, L1, L2;
if(C > 0)
L2.sum = C, L2.cnt = 1;
for(int i = last[u]; i; i = E[i].nxt) {
int v = E[i].to;
if(v == p)
continue;
Info vres = DFS(v, u);
L2 =
std::max(L2 + vres.L0,
makeChain(L1 + vres.L1 + E[i].w));
L1 = std::max(L1 + vres.L0,
L0 + vres.L1 + E[i].w);
L0 = L0 + vres.L0;
}
return Info(
std::max(makeChain(L1), std::max(L0, L2)), L1);
}
int main() {
IO::init();
int n = read();
int k = read() + 1;
Int64 sum = 0;
for(int i = 1; i < n; ++i) {
int u = read();
int v = read();
int w = read();
addEdge(u, v, w);
addEdge(v, u, w);
sum += iabs(w);
}
if(k > 1)
sum = sum / (k - 1) + 1;
Int64 l = -sum, r = sum, ans = 0;
while(l <= r) {
Int64 m = (l + r) >> 1;
C = m;
Chain res = DFS(1, 0).L0;
if(res.cnt <= k)
l = m + 1, ans = res.sum - asInt64(m) * k;
else
r = m - 1;
if(res.cnt == k)
break;
}
printf("%lld\n", ans);
return 0;
}
Loading

0 comments on commit c0f6e06

Please sign in to comment.