NOIP模拟赛[补档]

图论: 差分约束, 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

研究二维世界中的事情总是非常有趣的.
现有这样一个二维世界, 雨水源源不断地从天空中竖直降下来. 我们用一个由两条线段组成的容器来接收雨水, 问最多可以接到多少雨水.
我们给出每一条线段的两个端点坐标, 请你计算出答案.
注意: 不保证两条线段相交.

rain

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);
 	}
}
posted @ 2018-10-03 16:20  Zeonfai  阅读(163)  评论(0编辑  收藏  举报