team2012-D3-1D
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
=== 0019 ===
{{{
树上的倍增,维护某一段的最大值、最小值、祖先减后辈的最大值、后辈减祖先的最大值
设,要从b访问a,且lca(a,b) = c
分情况:
1. a-->c的最大值减去c-->b的最小值
2. a-->c的最大值减去c
3. c减去c-->b的最小值
4. 对于从a-->c的路径内:
2.1 某一段中的后辈减祖先的最大值
2.2 a-->x的最大值减去某一段(设为x-->y)的最小值
5. 对于从c-->b的路径内:
3.1 某一段中的祖先减后辈的最大值
3.2 某一段(设为x-->y)的最大值减去y-->b的最小值
}}}
{{{
int gao(int a, int bb) {
int c = lca(a, bb);
int la = dis[a] - dis[c], lb = dis[bb] - dis[c];
int ret = 0;
int maxa = 0, minb = 0x7FFFFFFF;
for (int i = maxf-1; i >= 0; --i) {
if ((la >> i) & 0x1) {
ret = max(ret, max(dmax[a][i], maxa - bmin[a][i]));
maxa = max(maxa, bmax[a][i]);
a = fa[a][i];
}
if ((lb >> i) & 0x1) {
ret = max(ret, max(umax[bb][i], bmax[bb][i] - minb));
minb = min(minb, bmin[bb][i]);
bb = fa[bb][i];
}
}
ret = max(ret, maxa-minb);
ret = max(ret, max(maxa-b[c], b[c]-minb));
return ret;
}
}}}
=== 0020 ===
{{{
f[i]:到第i关时,所能杀掉的最大boss数,以及在此条件下,耗费最少的potion数(为了方便pair比较,记为负数)
f[i] = max(跳过这个boss时:f[i-1],
要杀掉这个boss时:不妨设从第j+1关到第i关恰好能够攒到5个XX,并且杀掉第i关的boss最少耗费药水potion
此时的PII为(f[j].first+1,f[j].second-potion))
如何计算打boss最少耗费的药水:
如果P >= d[i],那么一直模拟,hp不够的时候加一个potion
否则P < d[i],那么贪心,被打完了就加一个potion,如果目前剩下的hp>剩下的回合数×d[i],那么可以return了
}}}
=== 0021 ===
额。。。求凸包
{{{
int graham(int n, point * p, point * ch, bool comEdge = false){
if(n < 3){ for(int i = 0; i < n; i++) ch[i] = p[i]; return n; }
const double e1 = comEdge ? eps : -eps; int i, j, k;
sort(p, p + n); ch[0] = p[0]; ch[1] = p[1];
for(i = j = 2; i < n; ch[j++] = p[i++]) while(j > 1 && (ch[j - 2] - ch[j - 1]) * (p[i] - ch[j - 1]) > e1) j--;
ch[k = j++] = p[n - 2];
for(i = n - 3; i > 0; ch[j++] = p[i--]) while(j > k && (ch[j - 2] - ch[j - 1]) * (p[i] - ch[j - 1]) > e1) j--;
while (j > k && (ch[j - 2] - ch[j - 1]) * (ch[0] - ch[j - 1]) > e1) j--;
return j;
}
}}}
=== 0022 ===
{{{
蘑菇题
按照题目的意思递归模拟即可
注意:
1. a,b,c和e,f,g的条件是并列的,优先级递降,而不是搞完a再搞b
2. 那个goal difference竟然要算负数-__-,翻译不是净胜球嘛,为什么输了也算。。。直接p1-p2即可,不用和0作比较
}}}
=== 0023 ===
{{{
首先确定m个点
然后二分答案,贪心判断
如何确定m个点:
不妨设:n = k * m + r
那么就把n个点分成了k块,剩下r个点
然后我们发现,当k是偶数时,无论中间选择哪一个点作为仓库(?),两端到它的距离都不相等,这就造成了浪费
于是当k是偶数时,令 n = (k-1) * m + (m+r)
然后最前面的都是每块k个点,最后面的都是每块k+2个点,中间可能有一块有k+1个点
}}}
{{{
int k = n / m;
int r = n % m;
if ((k&0x1) == 0) k--,r+=m;
p[0] = (k-1)/2;
int t = m-(r+1)/2;
for (int i = 1; i < t; i++)
p[i] = p[i-1] + k;
if (t > 0) p[t] = p[t-1] + k + (1-(r%2));
for (int i = t+1; i < m; i++)
p[i] = p[i-1] + k + 2;
}}}
=== 0024 ===
{{{
先算出l[i],r[i],表示向左、向右最远可以推倒到哪一个
然后进行dp
对于向右推的操作,可以用一个priority_queue来维护
对于向左推的操作,记录fl[i] = min(f[i],f[i-1],...,f[l[i]]) 并随时更新
}}}
{{{
// left
if (f[i] < 0 || f[i] > f[l[i]-1]+1) f[i] = f[l[i]-1]+1;
int z = i-1;
while (z >= l[i]) {
int fj = fl[z];
if (f[i] < 0 || f[i] > fj + 1) {
f[i] = fj + 1; fl[i] = fj;
if (fl[i] < 0 || fl[i] > fj) fl[i] = fj;
}
z = l[z] - 1;
}
if (fl[i] < 0 || fl[i] > f[i]) fl[i] = f[i];
}}}
0019
{{{
树上的倍增,维护某一段的最大值、最小值、祖先减后辈的最大值、后辈减祖先的最大值
设,要从b访问a,且lca(a,b) = c
分情况:
1. a-->c的最大值减去c-->b的最小值
2. a-->c的最大值减去c
3. c减去c-->b的最小值
4. 对于从a-->c的路径内:
2.1 某一段中的后辈减祖先的最大值
2.2 a-->x的最大值减去某一段(设为x-->y)的最小值
5. 对于从c-->b的路径内:
3.1 某一段中的祖先减后辈的最大值
3.2 某一段(设为x-->y)的最大值减去y-->b的最小值
}}}
{{{
int gao(int a, int bb) {
int c = lca(a, bb);
int la = dis[a] - dis[c], lb = dis[bb] - dis[c];
int ret = 0;
int maxa = 0, minb = 0x7FFFFFFF;
for (int i = maxf-1; i >= 0; --i) {
if ((la >> i) & 0x1) {
ret = max(ret, max(dmax[a][i], maxa - bmin[a][i]));
maxa = max(maxa, bmax[a][i]);
a = fa[a][i];
}
if ((lb >> i) & 0x1) {
ret = max(ret, max(umax[bb][i], bmax[bb][i] - minb));
minb = min(minb, bmin[bb][i]);
bb = fa[bb][i];
}
}
ret = max(ret, maxa-minb);
ret = max(ret, max(maxa-b[c], b[c]-minb));
return ret;
}
}}}
0020
{{{
f[i]:到第i关时,所能杀掉的最大boss数,以及在此条件下,耗费最少的potion数(为了方便pair比较,记为负数)
f[i] = max(跳过这个boss时:f[i-1],
要杀掉这个boss时:不妨设从第j+1关到第i关恰好能够攒到5个XX,并且杀掉第i关的boss最少耗费药水potion
此时的PII为(f[j].first+1,f[j].second-potion))
如何计算打boss最少耗费的药水:
如果P >= d[i],那么一直模拟,hp不够的时候加一个potion
否则P < d[i],那么贪心,被打完了就加一个potion,如果目前剩下的hp>剩下的回合数×d[i],那么可以return了
}}}
0021
额。。。求凸包
{{{
int graham(int n, point * p, point * ch, bool comEdge = false){
if(n < 3){ for(int i = 0; i < n; i++) ch[i] = p[i]; return n; }
const double e1 = comEdge ? eps : -eps; int i, j, k;
sort(p, p + n); ch[0] = p[0]; ch[1] = p[1];
for(i = j = 2; i < n; ch[j++] = p[i++]) while(j > 1 && (ch[j - 2] - ch[j - 1]) * (p[i] - ch[j - 1]) > e1) j--;
ch[k = j++] = p[n - 2];
for(i = n - 3; i > 0; ch[j++] = p[i--]) while(j > k && (ch[j - 2] - ch[j - 1]) * (p[i] - ch[j - 1]) > e1) j--;
while (j > k && (ch[j - 2] - ch[j - 1]) * (ch[0] - ch[j - 1]) > e1) j--;
return j;
}
}}}
0022
{{{
蘑菇题
按照题目的意思递归模拟即可
注意:
1. a,b,c和e,f,g的条件是并列的,优先级递降,而不是搞完a再搞b
2. 那个goal difference竟然要算负数-__-,翻译不是净胜球嘛,为什么输了也算。。。直接p1-p2即可,不用和0作比较
}}}
0023
{{{
首先确定m个点
然后二分答案,贪心判断
如何确定m个点:
不妨设:n = k * m + r
那么就把n个点分成了k块,剩下r个点
然后我们发现,当k是偶数时,无论中间选择哪一个点作为仓库(?),两端到它的距离都不相等,这就造成了浪费
于是当k是偶数时,令 n = (k-1) * m + (m+r)
然后最前面的都是每块k个点,最后面的都是每块k+2个点,中间可能有一块有k+1个点
}}}
int k = n / m;
int r = n % m;
if ((k&0x1) == 0) k--,r+=m;
p[0] = (k-1)/2;
int t = m-(r+1)/2;
for (int i = 1; i < t; i++)
p[i] = p[i-1] + k;
if (t > 0) p[t] = p[t-1] + k + (1-(r%2));
for (int i = t+1; i < m; i++)
p[i] = p[i-1] + k + 2;
0024
{{{
先算出l[i],r[i],表示向左、向右最远可以推倒到哪一个
然后进行dp
对于向右推的操作,可以用一个priority_queue来维护
对于向左推的操作,记录fl[i] = min(f[i],f[i-1],...,f[l[i]]) 并随时更新
}}}
{{{
// left
if (f[i] < 0 || f[i] > f[l[i]-1]+1) f[i] = f[l[i]-1]+1;
int z = i-1;
while (z >= l[i]) {
int fj = fl[z];
if (f[i] < 0 || f[i] > fj + 1) {
f[i] = fj + 1; fl[i] = fj;
if (fl[i] < 0 || fl[i] > fj) fl[i] = fj;
}
z = l[z] - 1;
}
if (fl[i] < 0 || fl[i] > f[i]) fl[i] = f[i];
}}}