图论: 差分约束, 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} \rfloor, \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(2b, n, p) = f(b, n, p) + f(b, n, p \oplus(b \& 1)) + \sum_{k = 0}^n (f(b, k, 0) + f(b, k, 1))f(b, n - k, p \oplus(b\& 1))
\]

对于已知所有\(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))\)

则有:

\[f(2b, n, p) = f(b, n, p) + f(b, n, p \oplus(b \& 1)) + \sum_{k = 0}^n x_k y_{n - k}
\]

我们注意到\(\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 \rfloor)\)即可.

计算所有\(\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('\n');
}
}
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 \times 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\n", (ans < 0 ? - ans : ans) + EPS);
}
}

NOIP模拟赛[补档]的更多相关文章

  1. NOI.AC NOIP模拟赛 第二场 补记

    NOI.AC NOIP模拟赛 第二场 补记 palindrome 题目大意: 同[CEOI2017]Palindromic Partitions string 同[TC11326]Impossible ...

  2. NOI.AC NOIP模拟赛 第一场 补记

    NOI.AC NOIP模拟赛 第一场 补记 candy 题目大意: 有两个超市,每个超市有\(n(n\le10^5)\)个糖,每个糖\(W\)元.每颗糖有一个愉悦度,其中,第一家商店中的第\(i\)颗 ...

  3. NOI.AC NOIP模拟赛 第四场 补记

    NOI.AC NOIP模拟赛 第四场 补记 子图 题目大意: 一张\(n(n\le5\times10^5)\)个点,\(m(m\le5\times10^5)\)条边的无向图.删去第\(i\)条边需要\ ...

  4. NOI.AC NOIP模拟赛 第三场 补记

    NOI.AC NOIP模拟赛 第三场 补记 列队 题目大意: 给定一个\(n\times m(n,m\le1000)\)的矩阵,每个格子上有一个数\(w_{i,j}\).保证\(w_{i,j}\)互不 ...

  5. Nescafe #29 NOIP模拟赛

    Nescafe #29 NOIP模拟赛 不知道这种题发出来算不算侵权...毕竟有的题在$bz$上是权限题,但是在$vijos$似乎又有原题...如果这算是侵权的话请联系我,我会尽快删除,谢谢~ 今天开 ...

  6. 【HHHOJ】NOIP模拟赛 捌 解题报告

    点此进入比赛 得分: \(30+30+70=130\)(弱爆了) 排名: \(Rank\ 22\) \(Rating\):\(-31\) \(T1\):[HHHOJ260]「NOIP模拟赛 捌」Dig ...

  7. NOIP模拟赛20161022

    NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...

  8. contesthunter暑假NOIP模拟赛第一场题解

    contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...

  9. NOIP模拟赛 by hzwer

    2015年10月04日NOIP模拟赛 by hzwer    (这是小奇=> 小奇挖矿2(mining) [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿 ...

随机推荐

  1. AngularJs开发——控制器间的通信

    AngularJs开发——控制器间的通信 指令与控制器之间通信,无非是以下几种方法: 基于scope继承的方式 基于event传播的方式 service的方式 基于scope继承的方式 最简单的让控制 ...

  2. The base command for the Docker CLI.

    Description The base command for the Docker CLI. Child commands Command Description docker attach At ...

  3. 固定width但是有间隔

    <!DOCTYPE > <html> <head> <title></title> <meta name="name&quo ...

  4. oracle 数据库图形化工具 sqldeveloper

    1. 安装完成Oracle数据库,点击左下角[开始]菜单,在所有程序中打开[Oracle] 2. 在开始菜单,展开Oracle数据库,安装文件,然后打开[应用程序开发].可以看到[sqldevelop ...

  5. bzoj1855: [Scoi2010]股票交易 单调队列优化dp ||HDU 3401

    这道题就是典型的单调队列优化dp了 很明显状态转移的方式有三种 1.前一天不买不卖: dp[i][j]=max(dp[i-1][j],dp[i][j]) 2.前i-W-1天买进一些股: dp[i][j ...

  6. cobalt strike使用笔记

    启动: ./teamserver 192.168.74.1 admin #启动cs服务器.admin为密码. 监听器: windows/beacon_dns/reverse_dns_txtwindow ...

  7. hashlib模块加密用法

    hashlib 加密模块 hashlib.md5() 构建一个md5的对象,用于调用对象的update方法去加密   例子: import hashlib hash = hashlib.md5() h ...

  8. ubuntu安装mysqlclient

    安装mysql: sudo apt-get install mysql-server mysql-client 然后mysql -V查看mysql是否安装成功 sudo apt-get install ...

  9. HDU1010(dfs+剪枝)

    Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  10. JVM指令的使用深入详解

    原文地址:https://www.jb51.net/article/155293.htm 一.未归类系列A 此系列暂未归类. 指令码    助记符                            ...