图论: 差分约束, 2 SAT
数据结构
字符串
数学: FFT / NTT / 线代
DP
计算几何
暴力
线性基 CF 724G
计划:
D1 T1: 斜率优化DP
D1 T2: 差分约束
D1 T3: 数据结构 + 字符串
D2 T1: FFT + DP
D2 T2: 计算几何
D2 T3: 莫比乌斯反演
数据生成(data.c/cpp/pas)
Time Limit: 3 seconds
Memory Limit: 256 megabytes
Description
现有一道题, 我们要给它出数据.
它的输入格式是这样的: 给定一个单调递增的序列(a_1 < a_2 < ... < a_n), 满足(n le max_n)且(a_n le max_a).
这道题的解法是: 找到(a_1)到(a_n)中所有数的最大公约数(d), 假如(frac{a_n}d - n)为偶数, 则输出"Bob"; 否则输出"Alice".
然而, 我们发现这道题目非常容易让不正确的程序水过, 因此我们希望生成一些数据, 能让下列的两种错误代码都输出错误答案:
- 用于判断奇偶性的数是(frac{a_n} d)
- 用于判断奇偶性的数是(a_n - n)
请你计算出在给定范围内可以产生的符合要求的数据组数. 由于这个数可能很大, 请输出这个数模(q)的余数.
Input
一行, 三个数: (max_n), (max_a), (q)
Output
一行答案.
Sample Input
3 6 1000
Sample Output
4
Hint
数据范围:
(
30 \%: \
max_n, max_a le 100 \
100 \%: \
1 le max_n le 30000 \
max_n le max_a le 10^9 \
10^4 le q le 10^5 + 126
)
题解
花絮: 题目描述中提及的那道题是Codeforces Round #201A
经过简单的推导, 我们发现, 对于一个符合要求的输入数据, 必须满足以下条件:
- (n)为奇数
- (a_n)为偶数
- (frac{a_n}d)为奇数
我们对最后一个结论进一步推导:
我们要选出的所有数都应是(2^k)的倍数, 并且使得(a_n)不是(2^{k + 1})的倍数.
这等效于选出(1 le a_i le lfloor frac{max_a}{2^k}
floor, space 1 le i le n)且(a_n)为奇数.
我们考虑用(f(b, n, p))来表示, 在([1, b])中挑选(n)个整数, 并且最后一个的奇偶性为(p)的方案数, 并设定边界: (f(1, 1, 1) = 1), 同时将(n = 0)的值设成(0), 以方便后续处理.
则我们有了如下递推式:
对于已知所有(f(b, n, p)), 要求所有(f(2b, n, p))的情况, 使用这个递归式的复杂度为(O(n^2)). 是否有优化的方法呢?
我们令(x_i = f(b, n, 0) + f(b, n, 1)), (y_i = f(b, j, p oplus (b & 1)))
则有:
我们注意到(sum x_k y_{n - k})是卷积的形式, 因此我们考虑用FFT处理.
我们又发现已知所有(f(b, n, p))的情况下, 求所有(f(b + 1, n, p))的时间复杂度为(O(n)), 因此, 我们要得到任意(f(b, n, p))的时间复杂度都不会超过(O(max_n log max_a log b)).
最后我们对于每一个(2^k), 统计(sum_{j = 1}^n f(lfloor frac{max_a}{2^k}, j, 1
floor))即可.
计算所有(frac{a_n}{2^k})合在一起算, 需要计算(log n)次, 因此正到题目的时间复杂度为(O(max_n log max_n log max_a)).
数据
存在(max_a = 1)的点, 需要特判. FFT可能要用long double.
标程
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
const int N = (int)3e4;
int n, a, q;
namespace convolution
{
int rev[N << 2], len;
inline int initialize(int n)
{
len = 1;
int tmp = 0;
for(; len < n << 1; ++ tmp, len <<= 1);
rev[0] = 0;
for(int i = 1; i < len; ++ i)
rev[i] = rev[i >> 1] >> 1 | (i & 1) << tmp - 1;
}
struct complex
{
long double rl, img;
inline complex() {}
inline complex(long double _rl, long double _img)
{
rl = _rl, img = _img;
}
inline complex friend operator +(complex a, complex b)
{
return complex(a.rl + b.rl, a.img + b.img);
}
inline complex friend operator -(complex a, complex b)
{
return complex(a.rl - b.rl, a.img - b.img);
}
inline complex friend operator *(complex a, complex b)
{
return complex(a.rl * b.rl - a.img * b.img, a.rl * b.img + b.rl * a.img);
}
}A[N << 2], B[N << 2];
long double PI = acos(-1);
inline void FFT(complex *a, int opt)
{
for(int i = 0; i < len; ++ i)
if(rev[i] < i)
std::swap(a[i], a[rev[i]]);
for(int i = 2; i <= len; i <<= 1)
{
complex omega_i = complex(cos(2 * PI * opt / i), sin(2 * PI * opt / i));
for(int j = 0; j < len; j += i)
{
complex omega = complex(1, 0);
for(int k = j; k < j + i / 2; ++ k)
{
complex u = a[k], t = a[k + i / 2] * omega;
a[k] = u + t, a[k + i / 2] = u - t;
omega = omega * omega_i;
}
}
}
if(opt == -1)
for(int i = 0; i < len; ++ i)
a[i].rl /= len;
}
inline void work(int *a, int *b, int n, long long *res)
{
memset(A, 0, sizeof(A)), memset(B, 0, sizeof(B));
for(int i = 0; i < n; ++ i)
A[i] = complex(a[i], 0), B[i] = complex(b[i], 0);
FFT(A, 1), FFT(B, 1);
for(int i = 0; i < len; ++ i)
A[i] = A[i] * B[i];
FFT(A, -1);
for(int i = 0; i < len; ++ i)
res[i] = (long long)(A[i].rl + 0.5);
}
}
int f[N + 1][2], _f[N + 1][2];
int ans;
inline void update()
{
for(int i = 1; i <= n; i += 2)
ans = (ans + f[i][1]) % q;
}
void work(int b)
{
if(b == 1)
{
memset(f, 0, sizeof(f));
f[1][1] = 1;
update();
return;
}
work(b / 2);
static int x[N + 1], y[N + 1];
std::swap(f, _f);
for(int i = 0; i <= n; ++ i)
x[i] = (_f[i][0] + _f[i][1]) % q;
for(int i = 0; i < 2; ++ i)
{
for(int j = 0; j <= n; ++ j)
y[j] = _f[j][i ^ (b >> 1 & 1)];
static long long res[N << 2];
convolution::work(x, y, n + 1, res);
for(int j = 0; j <= n; ++ j)
f[j][i] = (res[j] + _f[j][i] + _f[j][i ^ (b >> 1 & 1)]) % q;
}
if(b & 1)
{
std::swap(f, _f);
for(int i = 0; i < 2; ++ i)
{
f[0][i] = _f[0][i];
for(int j = 1; j <= n; ++ j)
f[j][i] = (_f[j][i] + (i ^ (b & 1) ? 0 : (j == 1 ? 1 : _f[j - 1][0] + _f[j - 1][1]))) % q;
}
}
update();
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("CF773F.in", "r", stdin);
// freopen("CF773F.out", "w", stdout);
#endif
using namespace std;
cin >> n >> a >> q;
if(a == 1)
{
puts("0");
return 0;
}
convolution::initialize(n + 1);
ans = 0;
work(a / 2);
cout << ans << endl;
}
说唱天王(rap.c/cpp/pas)
Memory limit: 256 megabytes
Time limit: 2 seconds
Description
一个二货号称自己是说唱天王.
我们甭管他是真的说唱天王, 还是假的说唱天王, 反正他要你协助他作曲. 我们也甭管他是怎么作曲的, 总之你的任务是这样的: 给定一棵树, 每条树边都代表一个字母. 我们用一个有序整数对((u, v)), 表示从编号为(u)的节点到编号为(v)的节点的最短路径上的边组成的字符串.
对于每个询问, 给定一个有序整数对((u, v
)), 请你输出, 在整棵树上可以找到多少个(w), 使得((u, w) < (u, v)), 也就是从(u)到(w)组成的字符串的字典序小于从(u)到(v)组成的字符串.
Input
第一行两个数(n)和(q), 表示树的点数和询问个数
接下来的(n - 1)行, 每行表示树上的一条边, 用两个整数(u)和(v)和一个字符(c)表示, 表示编号为(u)的点与编号为(v)的点之间有一条连边, 其对应的字母为(c).
接下来(q)行, 每行两个整数(u), (v), 表示询问中的有序整数对((u, v)).
Output
(q)行.
对于每个询问, 输出一个整数, 即答案.
Sample 1
input:
4 3
4 1 t
3 2 p
1 2 s
3 2
1 3
2 1
output:
0
1
1
Sample 2
input:
8 4
4 6 p
3 7 o
7 8 p
4 5 d
1 3 o
4 3 p
3 2 e
8 6
3 7
8 1
4 3
output:
6
1
3
1
Hint
数据范围:
(n, q le 20000)
(1 le u, v le n)
(c)为小写字母
题解
树分治.
对于每一次分治, 我们从分治重心开始DFS, 建立一棵trie树. 先序遍历这一棵trie树, 得到一个DFS序. 用树状数组维护即可.
详细的题解看这里:
http://codeforces.com/blog/entry/51163
代码
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#include <map>
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1;
char c;
while(! isdigit(c = getchar()))
if(c == '-')
sgn *= -1;
while(isdigit(c))
a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
inline char getChar()
{
char c;
while(! isalpha(c = getchar()));
return c;
}
inline void print(int a)
{
if(! a)
return;
print(a / 10);
putchar('0' + a % 10);
}
inline void println(int a)
{
if(a < 0)
putchar('-'), a *= -1;
if(a == 0)
putchar('0');
print(a);
putchar('
');
}
}
const int N = (int)2e4, K = 47, LOG = 15, MOD = 998244353, Q = (int)2e4;
int n, q;
int pw[N], pwInv[N];
inline int getInverse(int a)
{
int res = 1;
for(int i = MOD - 2; i; a = (long long)a * a % MOD, i >>= 1)
if(i & 1)
res = (long long)res * a % MOD;
return res;
}
int anc[N + 1][LOG], up[N + 1], dwn[N + 1], dep[N + 1];
struct query
{
int u, v, LCA, ans;
inline query()
{
ans = 0;
}
}qry[Q];
inline int getLCA(int id)
{
int u = qry[id].u, v = qry[id].v;
if(dep[u] < dep[v])
std::swap(u, v);
for(int i = LOG - 1; ~ i; -- i)
if(dep[u] - (1 << i) >= dep[v])
u = anc[u][i];
if(u == v)
return u;
for(int i = LOG - 1; ~ i; -- i)
if(anc[u][i] ^ anc[v][i])
u = anc[u][i], v = anc[v][i];
return anc[u][0];
}
struct binaryIndexedTree
{
int a[N + 1];
inline void build(int bnd)
{
for(int i = 1; i <= bnd; ++ i)
if(i + (i & - i) <= bnd)
a[i + (i & - i)] += a[i];
}
inline void modify(int pos, int x, int bnd)
{
for(int i = pos; i <= bnd; i += i & - i)
a[i] += x;
}
inline int query(int pos)
{
if(pos <= 0)
return 0;
int res = 0;
for(int i = pos; i; i -= i & - i)
res += a[i];
return res;
}
}BIT;
struct trieTree
{
struct node
{
node *suc[27];
int cnt, dfn, ed;
inline node()
{
for(int i = 1; i <= 26; ++ i)
suc[i] = NULL;
cnt = 0;
}
}*rt;
void clear(node *u)
{
for(int i = 1; i <= 26; ++ i)
if(u->suc[i] != NULL)
clear(u->suc[i]);
delete u;
}
inline clear()
{
if(rt != NULL)
clear(rt);
rt = new node;
}
int clk;
void DFS(node *u)
{
u->ed = u->dfn = ++ clk;
BIT.a[u->dfn] = u->cnt;
for(int i = 1; i <= 26; ++ i)
if(u->suc[i] != NULL)
DFS(u->suc[i]), u->ed = u->suc[i]->ed;
}
inline void DFS()
{
clk = 0;
DFS(rt);
BIT.build(clk);
}
}trie;
struct tree
{
struct node;
struct edge
{
node *v;
int c;
inline edge(node *_v, int _c)
{
v = _v, c = _c;
}
};
struct node
{
std::vector<edge> edg;
std::vector<int> qry;
int vst, sz, mx;
int up, dwn, dep;
node *anc[LOG];
trieTree::node *pos;
inline node()
{
edg.clear(), qry.clear(), vst = 0;
}
}nd[N + 1];
inline void addEdge(int u, int v, char c)
{
nd[u].edg.push_back(edge(nd + v, c - 'a' + 1)), nd[v].edg.push_back(edge(nd + u, c - 'a' + 1));
}
void DFS(int u, int pre, int c)
{
dep[u] = dep[pre] + 1;
up[u] = ((long long)up[pre] * K + c) % MOD, dwn[u] = (dwn[pre] + (long long)c * pw[dep[u] - 1]) % MOD;
anc[u][0] = pre;
for(int i = 1; i < LOG; ++ i)
anc[u][i] = anc[anc[u][i - 1]][i - 1];
for(auto edg : nd[u].edg)
if(edg.v - nd != pre)
DFS(edg.v - nd, u, edg.c);
}
inline void getDoublingTable()
{
up[1] = dwn[1] = 0;
dep[1] = -1;
DFS(1, 1, 0);
}
void getSize(node *u, node *pre)
{
u->sz = 1;
for(auto edg : u->edg)
if(! edg.v->vst && edg.v != pre)
getSize(edg.v, u), u->sz += edg.v->sz;
}
node* getRoot(node *u, node *pre, node *tp)
{
u->mx = tp->sz - u->sz;
for(auto edg : u->edg)
if(! edg.v->vst && edg.v != pre)
u->mx = std::max(u->mx, edg.v->sz);
node *res = u;
for(auto edg : u->edg)
if(! edg.v->vst && edg.v != pre)
{
node *tmp = getRoot(edg.v, u, tp);
if(tmp->mx < res->mx)
res = tmp;
}
return res;
}
std::map<int, trieTree::node*> mp;
void DFS(node *u, node *pre, int c)
{
u->dep = pre->dep + 1;
u->up = ((long long)pre->up * K + c) % MOD;
u->anc[0] = pre;
for(int i = 1; i < LOG; ++ i)
u->anc[i] = u->anc[i - 1]->anc[i - 1];
u->pos = (pre->pos->suc[c] == NULL ? pre->pos->suc[c] = new trieTree::node : pre->pos->suc[c]);
++ u->pos->cnt;
mp[u->dwn = (pre->dwn + (long long)c * pw[u->dep - 1]) % MOD] = u->pos;
u->sz = 1;
for(auto edg : u->edg)
if(! edg.v->vst && edg.v != pre)
DFS(edg.v, u, edg.c), u->sz += edg.v->sz;
}
void modify(node *u, node *pre, int x)
{
BIT.modify(u->pos->dfn, x, trie.clk);
for(auto edg : u->edg)
if(! edg.v->vst && edg.v != pre)
modify(edg.v, u, x);
}
inline int getHash(int id, int L, int R)
{
if(R > dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1))
return -1;
int u, v;
if(R <= dep[qry[id].u] - dep[qry[id].LCA])
{
u = qry[id].u;
for(int i = LOG - 1; ~ i; -- i)
if(1 << i <= L)
u = anc[u][i], L -= 1 << i;
v = qry[id].u;
for(int i = LOG - 1; ~ i; -- i)
if(1 << i <= R)
v = anc[v][i], R -= 1 << i;
return (up[u] - (long long)up[v] * pw[dep[u] - dep[v]] % MOD + MOD) % MOD;
}
else if(L <= dep[qry[id].u] - dep[qry[id].LCA] && R > dep[qry[id].u] - dep[qry[id].LCA])
{
u = qry[id].u;
for(int i = LOG - 1; ~ i; -- i)
if(1 << i <= L)
u = anc[u][i], L -= 1 << i;
v = qry[id].v, R = dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1) - R;
for(int i = LOG - 1; ~ i; -- i)
if(1 << i <= R)
v = anc[v][i], R -= 1 << i;
return (up[u] - (long long)up[qry[id].LCA] * pw[dep[u] - dep[qry[id].LCA]] % MOD + MOD
+ (long long)(dwn[v] - dwn[qry[id].LCA] + MOD) * pwInv[dep[qry[id].LCA]] % MOD * pw[dep[u] - dep[qry[id].LCA]] % MOD) % MOD;
}
else if(L > dep[qry[id].u] - dep[qry[id].LCA])
{
u = qry[id].v, L = dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1) - L;
for(int i = LOG - 1; ~ i; -- i)
if(1 << i <= L)
u = anc[u][i], L -= 1 << i;
v = qry[id].v, R = dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1) - R;
for(int i = LOG - 1; ~ i; -- i)
if(1 << i <= R)
v = anc[v][i], R -= 1 << i;
return (long long)(dwn[v] - dwn[u] + MOD) * pwInv[dep[u]] % MOD;
}
}
node* cen;
int curSz;
void update(int id)
{
node *u = nd + qry[id].u;
int len = 0;
for(int i = LOG - 1; ~ i; -- i)
if(len + (1 << i) <= dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1))
if(1 << i <= u->dep && getHash(id, len, len + (1 << i)) == (u->up - (long long)u->anc[i]->up * pw[1 << i] % MOD + MOD) % MOD)
u = u->anc[i], len += 1 << i;
if(u != cen)
{
if((u->up - (long long)u->anc[0]->up * K % MOD + MOD) % MOD < getHash(id, len, len + 1))
qry[id].ans += cen->sz - curSz;
return;
}
int L = 0, R = dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1) - len;
int res;
while(L <= R)
{
int mid = L + R >> 1;
int hsh = getHash(id, len, len + mid);
if(mp.find(hsh) != mp.end())
L = mid + 1, res = mid;
else
R = mid - 1;
}
if(len + res == dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1))
qry[id].ans += BIT.query(mp[getHash(id, len, len + res)]->dfn - 1);
else
{
trieTree::node *u = mp[getHash(id, len, len + res)];
int c = getHash(id, len + res, len + res + 1);
trieTree::node *p = NULL;
for(int i = 1; i < c; ++ i)
if(u->suc[i] != NULL)
p = u->suc[i];
if(p == NULL)
qry[id].ans += BIT.query(mp[getHash(id, len, len + res)]->dfn);
else
qry[id].ans += BIT.query(p->ed);
}
}
void getAnswer(node *u, node *pre)
{
for(auto id : u->qry)
update(id);
for(auto edg : u->edg)
if(! edg.v->vst && edg.v != pre)
getAnswer(edg.v, u);
}
void work(node *u)
{
getSize(u, u);
cen = u = getRoot(u, u, u);
mp.clear();
trie.clear();
u->pos = trie.rt;
++ u->pos->cnt;
u->dep = u->up = u->dwn = 0;
mp[u->dwn] = u->pos;
for(int i = 0; i < LOG; ++ i)
u->anc[i] = u;
u->sz = 1;
for(auto edg : u->edg)
if(! edg.v->vst)
DFS(edg.v, u, edg.c), u->sz += edg.v->sz;
trie.DFS();
BIT.modify(u->pos->dfn, -1, trie.clk);
for(auto id : u->qry)
update(id);
BIT.modify(u->pos->dfn, 1, trie.clk);
for(auto edg : u->edg)
if(! edg.v->vst)
{
curSz = edg.v->sz;
modify(edg.v, u, -1);
getAnswer(edg.v, u);
modify(edg.v, u, 1);
}
u->vst = 1;
for(auto edg : u->edg)
if(! edg.v->vst)
work(edg.v);
}
inline void decomposition()
{
work(nd + 1);
}
}T;
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
#endif
using namespace Zeonfai;
n = getInt(), q = getInt();
for(int i = 1; i < n; ++ i)
{
int u = getInt(), v = getInt();
char c = getChar();
T.addEdge(u, v, c);
}
pw[0] = pwInv[0] = 1;
for(int i = 1; i < n; ++ i)
pw[i] = (long long)pw[i - 1] * K % MOD, pwInv[i] = getInverse(pw[i]);
T.getDoublingTable();
for(int i = 0; i < q; ++ i)
T.nd[qry[i].u = getInt()].qry.push_back(i), qry[i].v = getInt(), qry[i].LCA = getLCA(i);
T.decomposition();
for(int i = 0; i < q; ++ i)
println(qry[i].ans);
}
关卡(game.c/cpp/pas)
Time Limit: 1 second
Memory Limit: 256 megabytes
Description
现有这样一个游戏: 这个游戏有(n)个关卡, 每个关卡有两个属性: 一个整数(t_i), 用于随机挑选关卡, 以及一个boolean型(tag), 表示这个关卡是否已经被挑战成功. 你要将这些关卡分为(k)个连续的段, 每一段称为一组. 根据游戏的设定, 开始时, 我们把每个关卡的(tag)设定为(false), 也就是未完成, 并且读入每一个(t_i). 每次进行游戏时, 我们随机找到任意一个存在未完成关卡的组(X), 它将会在(X)中通过某种方式选出一个关卡让你挑战. 具体来说, 它会计算(X)中每个(tag)为(true)的关卡的(t_i)之和(sum), 同时找到(X)中从左起第一个(tag)为(false)的关卡(p), 将(sum)加上(t_p), 然后在所有(tag)为(true)的关卡以及(p)中, 每个关卡被选出来让你挑战的机率为(frac{t_i}{sum}). 一个关卡只要被选出来让你挑战, 你就必须接受挑战, 无论之前你是否已经将其挑战成功过. 假如你将一个原本未完成的关卡挑战成功, 则这个关卡的(tag)会变成(true).
现在, 我们假设你的水平高超, 挑战任意一个关卡都必定能成功, 并且需要花费一个单位的时间. 那么, 我们希望知道, 通过合理地对所有关卡进行分组, 你完成所有关卡(也就是将所有关卡的(tag)变为(true))的期望时间最小是多少?
Input
两行.
第一行: 两个数, (n)表示有(n)个关卡, (k)表示要分成(k)组.
第二行: (n)个数, 分别为(t_1)到(t_n)
Output
一个浮点数, 精确到小数点后四位.
Sample 1
Input
4 2
100 3 5 7
Output
5.7429
Sample 2
Input
6 2
1 2 4 8 16 32
Output
8.5000
Hint
( 1 le n le 2 imes 10^5 \ 1 le k le min(n, 50) \ 1 le t_i le 10^5 )
Solution
斜率优化.
略.
Code
#include <cstdio>
#include <cctype>
#include <algorithm>
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1;
char c;
while(! isdigit(c = getchar()))
if(c == '-')
sgn *= -1;
while(isdigit(c))
a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
}
const int N = (int)2e5;
const double INF = 1e30;
static double sum[N + 1], a[N + 1], b[N + 1], f[N + 1], _f[N + 1];
inline double slope(int i, int j)
{
return ((_f[i] - a[i] + sum[i] * b[i]) - (_f[j] - a[j] + sum[j] * b[j])) / (sum[i] - sum[j]);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("CF674C.in", "r", stdin);
#endif
using namespace Zeonfai;
int n = getInt(), k = getInt();
static int t[N + 1];
for(int i = 1; i <= n; ++ i)
t[i] = getInt();
a[0] = b[0] = sum[0] = 0;
for(int i = 1; i <= n; ++ i)
sum[i] = sum[i - 1] + t[i], a[i] = a[i - 1] + sum[i] / t[i], b[i] = b[i - 1] + (double)1 / t[i];
for(int i = 0; i <= n; ++ i)
f[i] = a[i];
for(int i = 1; i < k; ++ i)
{
std::swap(f, _f);
static int que[N + 1];
int hd = 0, tl = 0;
for(int j = 0; j <= n; ++ j)
{
while(hd + 1 < tl && slope(que[hd + 1], que[hd]) < b[j])
++ hd;
if(tl > hd)
{
int k = que[hd];
f[j] = _f[k] + a[j] - a[k] - sum[k] * (b[j] - b[k]);
}
else
f[j] = INF;
while(hd + 1 < tl && slope(j, que[tl - 1]) < slope(que[tl - 1], que[tl - 2]))
tl --;
que[tl ++] = j;
}
}
printf("%.4lf", f[n]);
}
雨水收集器(rain.c/cpp/pas)
Memory limit: 128 megabytes
Time limit: 2 seconds
Description
研究二维世界中的事情总是非常有趣的.
现有这样一个二维世界, 雨水源源不断地从天空中竖直降下来. 我们用一个由两条线段组成的容器来接收雨水, 问最多可以接到多少雨水.
我们给出每一条线段的两个端点坐标, 请你计算出答案.
注意: 不保证两条线段相交.
Input
一个整数(n), 表示有(n)组询问
每组询问包含(8)个浮点数, 分为两组, 每组表示一条线段两个端点的坐标.
Output
一个浮点数, 保留小数点后(2)位, 表示答案.
Sample
input:
3
0 1 1 0
1 0 2 1
0 1 2 1
1 0 1 2
0 0 -0.5 0.5
1 1 2 3
output:
1.00
0.00
0.00
Hint
只有一组数据.
(n le 10^5)
每个坐标的数值(|p| le 1000)
Solution
对于答案不为(0)的情况, 我们直接计算, 这里不再赘述.
考虑什么情况下答案为(0):
- 两条线段不相交
- 靠上的一条线段完全覆盖下面的线段(也就是雨水进不去的情况)
其中第二种情况比较难考虑到.
Code
#include <cstdio>
#include <algorithm>
#include <cstdlib>
const double INF = 1e50;
const double EPS = 1e-8;
struct coordinate
{
double x, y;
inline coordinate() {}
inline coordinate(double _x, double _y)
{
x = _x, y = _y;
}
inline coordinate friend operator -(const coordinate &a, const coordinate &b)
{
return coordinate(a.x - b.x, a.y - b.y);
}
inline double friend operator ^(const coordinate &a, const coordinate &b)
{
return a.x * b.y - a.y * b.x;
}
};
struct line
{
coordinate p, q;
double k, b;
};
int flg;
inline coordinate cross(line a, line b)
{
if(((a.q - a.p) ^ (b.p - a.p)) * ((a.q - a.p) ^ (b.q - a.p)) > 0 || ((b.q - b.p) ^ (a.p - b.p)) * ((b.q - b.p) ^ (a.q - b.p)) > 0 || a.k == b.k)
{
puts("0.00");
flg = 1;
return coordinate();
}
coordinate res;
if(a.k != INF && b.k != INF)
res.x = (b.b - a.b) / (a.k - b.k), res.y = a.k * res.x + a.b;
else
{
if(a.k == INF)
std::swap(a, b);
res = coordinate(b.p.x, a.k * b.p.x + a.b);
}
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
#endif
int n;
for(scanf("%d", &n); n --;)
{
line a, b;
scanf("%lf%lf%lf%lf", &a.p.x, &a.p.y, &a.q.x, &a.q.y);
a.k = a.p.x == a.q.x ? INF : (a.q.y - a.p.y) / (a.q.x - a.p.x), a.b = a.p.y - a.p.x * a.k;
scanf("%lf%lf%lf%lf", &b.p.x, &b.p.y, &b.q.x, &b.q.y);
b.k = b.p.x == b.q.x ? INF : (b.q.y - b.p.y) / (b.q.x - b.p.x), b.b = b.p.y - b.p.x * b.k;
flg = 0;
coordinate crs = cross(a, b);
if(flg)
continue;
if(a.p.y < a.q.y)
std::swap(a.p, a.q);
if(b.p.y < b.q.y)
std::swap(b.p, b.q);
if((a.p.x < crs.x) == (b.p.x < crs.x) && (a.p.x < crs.x && (a.p.x < b.p.x) == (a.k < b.k) || a.p.x > crs.x && (a.p.x > b.p.x) == (a.k > b.k)))
{
puts("0.00");
continue;
}
if(a.p.y < b.p.y)
std::swap(a, b);
a.p.y = b.p.y, a.p.x = a.k == INF ? a.p.x : (a.p.y - a.b) / a.k;
double ans = ((a.p - crs) ^ (b.p - crs)) / 2;
printf("%.2lf
", (ans < 0 ? - ans : ans) + EPS);
}
}