• 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} 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(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 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

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

    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
    ", (ans < 0 ? - ans : ans) + EPS);
     	}
    }
    
  • 相关阅读:
    ubuntu修改主机名称+修改终端显示目录和计算机名称
    google 搜索 PPT
    就为在YouTube上下载个视频
    读书笔记 ---- 《嵌入式系统技术》
    WARNING: Unable to open an initial console
    Ubuntu ftp服务器搭建 + UltraEdit编辑FTP文件
    51Nod 1079 中国剩余定理【模板题】
    Leetcode 11 Container with most water【双指针】
    kuangbin专题六 最小生成树【从入门到熟练】【5题】
    kuangbin专题十二 基础DP1【从入门到熟练】【10题】
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/9740025.html
Copyright © 2020-2023  润新知