zoukankan      html  css  js  c++  java
  • BJOI2018

    BJOI2018

    省选挂完,是时候更一篇题解了。对于鬼畜结论题我只放结论不给证明,不要打我……

    day1

    二进制

    试题描述

    pupil 发现对于一个十进制数,无论怎么将其的数字重新排列,均不影响其是不是 (3) 的倍数。他想研究对于二进制,是否也有类似的性质。 于是他生成了一个长为 (n) 的二进制串,希望你对于这个二进制串的一个子区间,能求出其有多少位置不同的连续子串,满足在重新排列后(可包含前导 (0))是一个 (3) 的倍数。两个位置不同的子区间指开始位置不同或结束位置不同。 由于他想尝试尽量多的情况,他有时会修改串中的一个位置,并且会进行多次询问。

    输入

    输入第一行包含一个正整数 (n),表示二进制数的长度。

    之后一行 (n) 个空格隔开的整数,保证均是 (0)(1),表示该二进制串。

    之后一行一个整数 (m),表示询问和修改的总次数。

    之后 (m) 行每行为 (1 i),表示 pupil 修改了串的第 (i) 个位置((0) 变成 (1)(1) 变成 (0) ),或 (2 l r),表示 pupil 询问的子区间是 ([l,r])

    串的下标从 (1) 开始。

    输出

    对于每次询问,输出一行一个整数表示对应该询问的结果。

    输入示例

    4
    1 0 1 0
    3
    2 1 3
    1 3
    2 3 4
    

    输出示例

    2
    3
    

    数据规模及约定

    对于 (20\%) 的数据,(1 le n,m le 100)

    对于 (50\%) 的数据,(1le n,m le 5000)

    对于 (100\%) 的数据,(1 le n,m le 100000)(l le r)

    题解

    由于能任意重排,所以我们显然可以只关心 (1)(0) 的个数。

    考虑一个二进制数,从低到高每一位对 (3) 取模;发现它是“(121212 cdots)”这样排列的,那么二进制中每个 (1) 代表你有一次选择数的机会,你的目标是让选出的若干个 (1) 或者 (2) 最终总和是 (3) 的倍数。

    这样想的话,显然当 (1) 的个数为偶数的时候我们可以 (1)(2) 配对,所以可行;当 (1) 的个数为 (1) 时,无论如何都不可能凑数来;当 (1) 的个数为 (ge 3) 的奇数时,我们先将它从低到高位“密铺”,然后最后由于是奇数个肯定会多出一个 (1),这时我们考虑调整(将前面的一些 (2) 变成 (1)),你会发现将最后一个 (2) 变成 (1) 即可,但这样操作意味着你需要两个 (0) 来占位,故当 (1) 的个数为 (ge 3) 的奇数时,(0) 的个数必须 (ge 2) 才可以凑出来。

    现在问题就是如何维护这个东西。我们回忆一下之前应该都做过的一个简单问题:动态最大连续和,问题就是多组询问每次询问一个区间,求这个区间内所有子区间中和最大的是多少。这题我们是用线段树维护区间答案以及区间前后缀信息实现的;本题也可以这样做,我们令线段树上某个节点 (o) 上的前缀信息为 (o ightarrow pre[a][b]),表示 (0) 的个数为 (a)(1) 的个数为 (b) 的前缀的个数,同理后缀信息 (o ightarrow suf[a][b]) 也是如此,这样我们在合并的时候可以通过简单的加法得知 (0)(1) 的个数,从而获得想要的信息(注意这里的 (a)(b) 并不需要 (O(n)) 级别,因为我们只关心 (0) 的个数是否大于 (1),并且只关心 (1) 的个数的奇偶性以及是否大于 (1),所以 (a, b) 都是常数级别大小的)。

    代码写起来略微麻烦,但是如果定义一个类,然后将合并操作定义在加法中,写起来就会舒畅许多。

    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 100010
    #define LL long long
    #define pii pair <int, int>
    #define ppp pair <pii, pii >
    #define x first
    #define y second
    #define mp(x, y) make_pair(x, y)
    
    int A[maxn];
    
    void Plus(int &mj, int lj, int rj) {
    	mj = -1;
    	if(lj < 2 && rj < 2) mj = lj ^ rj;
    	if(lj < 2 && rj == 2) mj = lj;
    	if(lj < 2 && rj == 3) mj = lj ^ 1;
    	if(lj == 2 && rj < 2) mj = rj;
    	if(lj == 3 && rj < 2) mj = rj ^ 1;
    	if(lj == 2 && rj == 2) mj = 2;
    	if(lj == 2 && rj == 3) mj = 3;
    	if(lj == 3 && rj == 2) mj = 3;
    	if(lj == 3 && rj == 3) mj = 0;
    	return ;
    }
    ppp trans[500];
    int ct;
    void gettrans() {
    	rep(li, 0, 2) rep(lj, 0, 3)
    		rep(ri, 0, 2) rep(rj, 0, 3) {
    			int Mi = min(li + ri, 2), mj;
    			Plus(mj, lj, rj);
    			if(!(mj & 1) || (Mi == 2 && mj == 1)) trans[++ct] = mp(mp(li, lj), mp(ri, rj));
    		}
    	return ;
    }
    struct Info {
    	LL sumv;
    	int zero, one, pre[3][4], suf[3][4]; // [][2] means there's no 1, [][3] means there's exactly one 1.
    	
    	Info operator + (const Info& t) const {
    		Info ans;
    		ans.sumv = sumv + t.sumv;
    		rep(i, 1, ct) {
    			int li = trans[i].x.x, lj = trans[i].x.y, ri = trans[i].y.x, rj = trans[i].y.y;
    			ans.sumv += (LL)suf[li][lj] * t.pre[ri][rj];
    		}
    		ans.zero = min(zero + t.zero, 2);
    		Plus(ans.one, one, t.one);
    		memcpy(ans.pre, pre, sizeof(pre));
    		rep(ri, 0, 2) rep(rj, 0, 3) {
    			int Mi = min(zero + ri, 2), mj;
    			Plus(mj, one, rj);
    			ans.pre[Mi][mj] += t.pre[ri][rj];
    		}
    		memcpy(ans.suf, t.suf, sizeof(t.suf));
    		rep(li, 0, 2) rep(lj, 0, 3) {
    			int Mi = min(li + t.zero, 2), mj;
    			Plus(mj, lj, t.one);
    			ans.suf[Mi][mj] += suf[li][lj];
    		}
    		return ans;
    	}
    	
    	void upd(int x) {
    		sumv = !x;
    		zero = !x;
    		one = x ? 3 : 2;
    		memset(pre, 0, sizeof(pre));
    		memset(suf, 0, sizeof(suf));
    		pre[zero][one] = suf[zero][one] = 1;
    		return ;
    	}
    } seg[maxn<<2];
    
    void build(int o, int l, int r) {
    	if(l == r) return seg[o].upd(A[l]);
    	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
    	build(lc, l, mid); build(rc, mid + 1, r);
    	seg[o] = seg[lc] + seg[rc];
    	return ;
    }
    void update(int o, int l, int r, int p) {
    	if(l == r) return A[l] ^= 1, seg[o].upd(A[l]);
    	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
    	if(p <= mid) update(lc, l, mid, p);
    	else update(rc, mid + 1, r, p);
    	seg[o] = seg[lc] + seg[rc];
    	return ;
    }
    Info Ans;
    void query(int o, int l, int r, int ql, int qr) {
    	if(ql <= l && r <= qr) {
    		if(Ans.sumv == -1) Ans = seg[o];
    		else Ans = Ans + seg[o];
    		return ;
    	}
    	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
    	if(ql <= mid) query(lc, l, mid, ql, qr);
    	if(qr > mid) query(rc, mid + 1, r, ql, qr);
    	return ;
    }
    
    int main() {
    	freopen("binary.in", "r", stdin);
    	freopen("binary.out", "w", stdout);
    	
    	int n = read();
    	rep(i, 1, n) A[i] = read();
    	
    	gettrans();
    	build(1, 1, n);
    	int q = read();
    	while(q--) {
    		int tp = read(), l = read(), r;
    		if(tp == 2) r = read(), Ans.sumv = -1, query(1, 1, n, l, r), printf("%lld
    ", Ans.sumv);
    		else update(1, 1, n, l);
    	}
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    

    染色

    试题描述

    pupil 喜欢给图的顶点染颜色。有一天,master 想刁难他,于是给了他一个无重边和自环的无向图, 并且对每个点分别给了一个大小为 (2) 的颜色集合,pupil 只能从这个集合中选一种颜色给这个点染色。master 希望 pupil 的染色方案使得没有两个有边相连的点被染了相同的颜色。

    现在 pupil 想知道,是否无论 master 的颜色集合是什么,他均有办法按照要求染色。

    输入

    输入包含多组数据。

    第一行一个正整数 (T),表示数据组数。

    之后每组数据第一行两个空格隔开的整数 (n,m),表示这个无向图的点数和边数。

    之后 (m) 行,每行两个空格隔开的正整数 (i,j),表示图中的一条连接点 (i) 和点 (j) 的边。

    图的节点从 (1) 开始标号。

    输出

    对于每组数据,如果 pupil 无论如何均能染色,输出一行一个字符串 YES,否则输出一行一个字符串 NO

    输入示例

    3
    6 9
    1 2
    1 4
    1 6
    3 2
    3 4
    3 6
    5 2
    5 4
    5 6
    2 1
    1 2
    3 3
    1 2
    1 3
    2 3
    

    输出示例

    NO
    YES
    NO
    

    数据规模及约定

    对于 (10\%) 的数据,(1 le n le 3)

    对于 (20\%) 的数据,(1 le n le 6)

    对于 (50\%) 的数据,(1 le n le 1000)(0 le m le 2000)

    对于(100\%) 的数据,(1 le n le 10000)(0 le m le 20000)(1 le T le 10)

    题解

    这就是传说中的鬼畜结论题。结论如下:

    • 首先带奇环的显然是 NO
    • 否则对于每个连通块,递归删掉所有度数为 (1) 的点(即将支出去的外向树删掉),如果:
      • 剩下的是偶环,显然是 YES
      • 剩下的图只有两个度数为 (3) 的点,那么这两个点之间有 (3) 条路径,这三条路径上边数依次为 (2, 2, even)(even) 代表偶数)则是 YES
      • 其他情况全为 NO
    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 10010
    #define maxm 40010
    
    int n, m, head[maxn], nxt[maxm], to[maxm], deg[maxn];
    
    void AddEdge(int a, int b) {
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	deg[a]++; deg[b]++;
    	return ;
    }
    
    vector <int> con[maxn];
    int fa[maxn];
    int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); }
    
    int col[maxn];
    int dfs(int u, int c) {
    	col[u] = c;
    	for(int e = head[u]; e; e = nxt[e]) if(col[to[e]] && col[to[e]] == c) return -1;
    	for(int e = head[u]; e; e = nxt[e]) if(!col[to[e]]) {
    		if(dfs(to[e], 3 - c) < 0) return -1;
    	}
    	return 0;
    }
    
    int Q[maxn], hd, tl, step[maxn];
    bool vis[maxn];
    bool judge(int c) {
    	if(con[c].size() == 1) return 1;
    	
    	hd = tl = 0;
    	rep(i, 0, (int)con[c].size() - 1) {
    		int u = con[c][i];
    		if(deg[u] == 1) Q[++tl] = u, vis[u] = 1;
    	}
    	while(hd < tl) {
    		int u = Q[++hd];
    		for(int e = head[u]; e; e = nxt[e]) if(--deg[to[e]] == 1) Q[++tl] = to[e], vis[to[e]] = 1;
    	}
    	
    	int s = -1, t = -1;
    	rep(i, 0, (int)con[c].size() - 1) {
    		int u = con[c][i];
    		if(vis[u]) continue;
    		assert(deg[u] > 1);
    		if(deg[u] == 2) continue;
    		if(deg[u] > 3) return 0;
    		if(s < 0) s = u;
    		else if(t < 0) t = u;
    		else return 0;
    	}
    	if(s < 0) return 1;
    	
    	int two = 0, even = 0;
    	hd = tl = 0; Q[++tl] = s; step[s] = 0;
    	while(hd < tl) {
    		int u = Q[++hd];
    		for(int e = head[u]; e; e = nxt[e]) if(!vis[to[e]]) {
    			step[to[e]] = step[u] + 1;
    			if(to[e] == t) {
    				if(step[to[e]] == 2 && two < 2) two++;
    				else if(step[to[e]] & 1) return 0;
    				else even++;
    			}
    			else vis[to[e]] = 1, Q[++tl] = to[e];
    		}
    	}
    	if(two == 2 && even == 1) return 1;
    	return 0;
    }
    void work() {
    	int n = read(), M = read();
    	m = 0; memset(head, 0, sizeof(head));
    	memset(deg, 0, sizeof(deg));
    	rep(i, 1, n) fa[i] = i, con[i].clear();
    	rep(i, 1, M) {
    		int a = read(), b = read();
    		AddEdge(a, b);
    		int u = findset(a), v = findset(b);
    		if(u != v) fa[v] = u;
    	}
    	if(n <= 2) return (void)puts("YES");
    	
    	rep(i, 1, n) con[findset(i)].push_back(i);
    	
    	memset(col, 0, sizeof(col));
    	memset(vis, 0, sizeof(vis));
    	memset(step, 0, sizeof(step));
    	rep(i, 1, n) if(!col[i]) {
    		if(dfs(i, 1) < 0) return (void)puts("NO");
    		if(!judge(findset(i))) return (void)puts("NO");
    	}
    	puts("YES");
    	return ;
    }
    
    int main() {
    	freopen("color.in", "r", stdin);
    	freopen("color.out", "w", stdout);
    	
    	int T = read();
    	while(T--) work();
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    

    求和

    试题描述

    master 对树上的求和非常感兴趣。他生成了一棵有根树,并且希望多次询问这棵树上一段路径上所有节点深度的 (k) 次方和,而且每次的 (k) 可能是不同的。此处节点深度的定义是这个节点到根的路径上的边数。 他把这个问题交给了 pupil,但 pupil 并不会这么复杂的操作,你能帮他解决吗?

    输入

    第一行包含一个正整数 (n),表示树的节点数。

    之后 (n-1) 行每行两个空格隔开的正整数 (i,j),表示树上的一条连接点 (i) 和点 (j) 的边。

    之后一行一个正整数 (m),表示询问的数量。

    之后每行三个空格隔开的正整数 (i,j,k),表示询问从点 (i) 到点 (j) 的路径上所有节点深度的 (k) 次方和。由于这个结果可能非常大,输出其对 (998244353) 取模的结果。

    树的节点从 (1) 开始标号,其中 (1) 号节点为树的根。

    输出

    对于每组数据输出一行一个正整数表示取模后的结果。

    输入示例

    5
    1 2
    1 3
    2 4
    2 5
    2
    1 4 5
    5 4 45
    

    输出示例

    33
    503245989
    

    数据规模及约定

    对于 (30\%) 的数据,(1 le n,m le 100)

    对于 (60\%) 的数据,(1 le n,m le 1000)

    对于 (100\%) 的数据,(1 le n,m le 300000,1 le k le 50)

    题解

    这个题……才发现……它其实预处理 (50) 种权值即可。

    但是我用的是线性插值——因为它答案形如一段连续的自然数的 (k) 次方之和,所以是两个自然数幂求和相减。

    反正这题随便做都能 A 吧。

    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 300010
    #define maxm 600010
    #define maxlog 19
    #define maxk 100
    #define MOD 998244353
    #define LL long long
    
    int n;
    namespace Tree {
    	int m, head[maxn], nxt[maxm], to[maxm];
    	
    	void AddEdge(int a, int b) {
    		to[++m] = b; nxt[m] = head[a]; head[a] = m;
    		swap(a, b);
    		to[++m] = b; nxt[m] = head[a]; head[a] = m;
    		return ;
    	}
    	
    	int dep[maxn], fa[maxn][maxlog];
    	void build(int u) {
    		rep(i, 1, maxlog - 1) fa[u][i] = fa[fa[u][i-1]][i-1];
    		for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u][0]) {
    			dep[to[e]] = dep[u] + 1;
    			fa[to[e]][0] = u;
    			build(to[e]);
    		}
    		return ;
    	}
    	
    	int lca(int a, int b) {
    		if(dep[a] < dep[b]) swap(a, b);
    		dwn(i, maxlog - 1, 0) if(dep[a] - dep[b] >= (1 << i)) a = fa[a][i];
    		dwn(i, maxlog - 1, 0) if(fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
    		return a == b ? a : fa[b][0];
    	}
    }
    
    namespace Math {
    	const int klim = 50;
    	
    	int Pow(int a, int b) {
    		int ans = 1, t = a;
    		while(b) {
    			if(b & 1) ans = (LL)ans * t % MOD;
    			t = (LL)t * t % MOD; b >>= 1;
    		}
    		return ans;
    	}
    	
    	int Y[maxk][maxk], fac[maxk], ifac[maxk];
    	void getval() {
    		rep(k, 1, klim) {
    			Y[k][0] = 0;
    			rep(i, 1, k + 1) {
    				Y[k][i] = Y[k][i-1] + Pow(i, k);
    				if(Y[k][i] >= MOD) Y[k][i] -= MOD;
    			}
    		}
    		ifac[1] = 1;
    		rep(i, 2, klim + 1) ifac[i] = (LL)(MOD - MOD / i) * ifac[MOD%i] % MOD;
    		fac[0] = ifac[0] = 1;
    		rep(i, 1, klim + 1) fac[i] = (LL)fac[i-1] * i % MOD, ifac[i] = (LL)ifac[i-1] * ifac[i] % MOD;
    		return ;
    	}
    	
    	int pre[maxk], suf[maxk];
    	int calc(int k, int x) { // calculate 1^k + 2^k + ... + x^k
    		if(x <= 0) return 0;
    		int n = k + 1;
    		pre[0] = x;
    		rep(i, 1, n) pre[i] = (LL)pre[i-1] * (x - i + MOD) % MOD;
    		suf[n] = (x - n + MOD) % MOD;
    		dwn(i, n - 1, 0) suf[i] = (LL)suf[i+1] * (x - i + MOD) % MOD;
    		int ans = 0;
    		rep(i, 0, n) {
    			int tmp = (LL)(i ? pre[i-1] : 1) * (i < n ? suf[i+1] : 1) % MOD * ifac[i] % MOD * ifac[n-i] % MOD * Y[k][i] % MOD;
    			if(n - i & 1) ans -= tmp;
    			else ans += tmp;
    			if(ans >= MOD) ans -= MOD;
    			if(ans < 0) ans += MOD;
    		}
    		return ans;
    	}
    }
    
    int num[100], cntn;
    void putint(int x) {
    	if(!x) return (void)puts("0");
    	cntn = 0;
    	while(x) num[++cntn] = x % 10, x /= 10;
    	dwn(i, cntn, 1) putchar(num[i] + '0'); putchar('
    ');
    	return ;
    }
    
    int main() {
    	freopen("sum.in", "r", stdin);
    	freopen("sum.out", "w", stdout);
    	
    	n = read();
    	rep(i, 1, n - 1) {
    		int a = read(), b = read();
    		Tree::AddEdge(a, b);
    	}
    	
    	Tree::build(1);
    	Math::getval();
    	int q = read();
    	while(q--) {
    		int a = read(), b = read(), c = Tree::lca(a, b), k = read();
    		LL ans = (LL)Math::calc(k, Tree::dep[a]) - Math::calc(k, Tree::dep[c] - 1) + Math::calc(k, Tree::dep[b]) - Math::calc(k, Tree::dep[c]);
    		putint((ans % MOD + MOD) % MOD);
    	}
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    

    day2

    双人猜数游戏

    试题描述

    Alice 和 Bob 是一对非常聪明的人,他们可以算出各种各样游戏的最优策略。现在有个综艺节目《最强大佬》请他们来玩一个游戏。主持人写了三个正整数 (s)(m)(n) ,然后一起告诉 Alice 和 Bob (s le m le n) 以及 (s) 是多少。(即,(s) 是接 下来要猜的 (m)(n) 的下限。)之后主持人单独告诉 Alice (m)(n) 的乘积是多少, 单独告诉 Bob (m)(n) 的和是多少。

    当然,如果一个人同时知道 (m)(n) 的乘积以及 (m)(n) 的和话就能很容易地算出 (m)(n) 分别是多少,但现在 Alice 和 Bob 只分别知道其中一个,而且只分别知道其中一个,而且他们只能回答主持人的问题,不能交流。从 Alice 或 Bob(见输入)开始 依次询问 Alice/Bob 知不知道 (m)(n) 分别是多少, Alice/Bob 只能回答知道/不知道。

    为了节目效果,为了显示出 Alice 和 Bob 非常聪明,主持人希望 Alice 和 Bob 一共说了 ttt 次“不知道 ”以后两个人都知道 (m)(n) 是多少了 。现在主持人找到你,希望让帮他构造一组符合条件的 (m)(n)

    输入

    输入文件 guess*.in 共一行,格式为 (s) (t),其中 (s)(t) 的定义见题目描述(注意 Alice 和 Bob 知道 (s) 是多少), 为 Alice 或 Bob,表示主持人第一次问的人。

    输出

    输出文件 guess*.out 共一行两个数,以空格隔开表示一组满足要求的 (m)(n) 。若有多组解,输出 (m)(n) 的和最小那组解。若仍有多组解,输出 (m)(n) 的和最小解中 (m) 最小的那组解。

    输入数据保证有解。

    输入示例1

    5 Bob 2
    

    输出示例1

    6 10
    

    输入示例2

    2 Alice 3
    

    输出示例2

    4 4
    

    输入数据

    戳我下载

    数据规模及约定

    对于 (40\%) 的数据, (t = 2)

    对于 (100\%) 的数据, (1 le s le 200)(2 le t le 15),输入数据保证有解。

    题解

    由于是提交答案题,我们可以不管复杂度

    考虑给定一对 ((m, n)),如何计算需要经过多少个“不知道”。想象一下加入你是 Alice,拿到一个数 (sum) 你会怎么做;显然你会将它分解质因数,然后把所有可能的满足 (m, n ge s)(mn = sum) 的数对 ((m, n)) 列出来,依次排除;排除的方法就是,你站在 Bob 的角度,考虑 (m + n) 这个值他是否会回答不知道——到此为止,一个很明显的递归过程就出来了。

    注意判断一些边界情况,如当某个人回答了“知道”,但是另一个人还是不知道,这个时候那另一个人就永远不可能知道了;还有如果发现某一次有个人排除了所有方案,那么它既不该回答知道也不能回答不知道,只能说明上一次调用这层函数是一个错误,不需要考虑这个情况。

    以下放求解的程序。

    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '0') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 100000
    #define maxt 20
    
    int S = 1, f[2][maxt][maxn];
    
    bool know[maxt];
    int tr[2][maxn];
    int guess(int ab, int turn, int sum) {
    	if(sum >= maxn) return 0;
    	int &ans = f[ab][turn][sum];
    	if(ans >= 0) {
    		if(ans == 1) {
    			int nt = tr[ab][sum];
    			assert(nt);
    			if(nt != turn) assert(0);
    		}
    		return ans;
    	}
    	if(!turn) return ans = 0;
    	if(ab == 0) { // Alice
    		int m = sqrt(sum), cnt = 0;
    		rep(i, S, m) if(sum % i == 0) {
    			int j = sum / i;
    			if(guess(ab ^ 1, turn - 1, i + j) == know[turn-1]) cnt++;
    		}
    		if(!cnt) return ans = 2;
    		if(cnt == 1) {
    			if(tr[ab][sum]) return ans = tr[ab][sum] == turn ? 1 : 2;
    			tr[ab][sum] = turn;
    			return ans = 1;
    		}
    		return ans = 0;
    	}
    	if(ab == 1) { // Bob
    		int m = sum >> 1, cnt = 0;
    		rep(i, S, m) {
    			int j = sum - i;
    			if(guess(ab ^ 1, turn - 1, i * j) == know[turn-1]) cnt++;
    		}
    		if(!cnt) return ans = 2;
    		if(cnt == 1) {
    			if(tr[ab][sum]) return ans = tr[ab][sum] == turn ? 1 : 2;
    			tr[ab][sum] = turn;
    			return ans = 1;
    		}
    		return ans = 0;
    	}
    	assert(0);
    	return ans = 0;
    }
    
    char name[100];
    
    int main() {
    	S = read(); scanf("%s", name); int t = read();
    	for(int sum = S << 1; ; sum++) {
    		rep(i, S, sum >> 1) {
    			int j = sum - i, cno = 0, ab = name[0] == 'A' ? 0 : 1;
    			bool has = 0;
    			memset(know, 0, sizeof(know));
    			memset(f, -1, sizeof(f));
    			memset(tr, 0, sizeof(tr));
    			for(int turn = 1; ; turn++, ab ^= 1) {
    				int a = guess(ab, turn, ab ? i + j : i * j);
    				assert(a < 2);
    				know[turn] = a;
    				cno += !a;
    				if(cno > t) break;
    				if(has && !a) { cno = -1; break; }
    				if(has && a) break;
    				has |= a;
    			}
    			if(cno == t) return printf("%d %d
    ", i, j), 0;
    		}
    	}
    	
    	return 0;
    }
    

    链上二次求和

    试题描述

    有一条长度为 (n) 的链((forall 1 le i < n),点 (i) 与点 (i+1) 之间有一条边的无向图), 每个点有一个整数权值,第 (i) 个点的权值是 (a_i)。现在有 (m) 个操作,每个操作如下:

    操作 (1)(修改):给定链上两个节点 (u)(v) 和一个整数 (d),表示将链上 (u)(v) 唯一的简单路径上每个点权值都加上 (d)

    操作 (2)(询问):给定两个正整数 (l)(r),表示求链上所有节点个数大于等于 (l) 且小于等于 (r) 的简单路径节点权值和之和。由于答案很大,只用输出对质数 (1000000007) 取模的结果即可。

    一条节点个数为 (k) 的简单路径节点权值和为这条上所有 (k) 个节点(包括端点)的权值之和,而本题中要求是对所有满足要求的简单路径,求这一权值和的和。

    由于是无向图,路径也是无向的,即点 (1) 到点 (2) 的路径与点 (2) 到点 (1) 的路径是同一条,不要重复计算。

    输入

    输入第一行包含两个正整数 (n)(m),分别表示节点个数和操作次数。

    第二行包含 (n) 个整数,其中第 (i) 个数 (a_i) 为第 (i) 个点的初始权值。

    接下来 (m) 行,每行为 (1 u v d)(2 l r) 的形式,分别表示进行一次操作 (1)(修改)或操作 (2)(询问)。

    输出

    对于每次询问,输出一行一个整数,表示答案对 (1000000007) 取模的余数。

    输入示例

    5 5
    1 1 1 1 1
    2 5 5
    2 1 2
    1 1 2 2
    2 1 1
    1 1 5 3
    

    输出示例

    5
    13
    9
    

    数据规模及约定

    记操作 (1)(修改)的次数为 (m')

    对于全部数据, 保证 (n le 200000, m le 500000, m' le 100000, 0 le a_i < 1000000007)

    (1 le u le n, 1le v le n, 0 le d < 1000000007, l le r le n)

    题解

    首先考虑如何解决不带修改的问题,不带修改就是每次询问求这么个东西

    [sum_{k=l}^r sum_{i=1}^{n-k+1} sum_{j=i}^{i+k-1} A_j ]

    我们依次令 (S_i = sum_{j=1}^i A_j, SS_i = sum_{j=1}^i S_i, SSS_i = sum_{j=1}^i SS_j),然后一步步化简以上式子(下标 (le 0) 时值均为 (0))。

    [egin{aligned} & sum_{k=l}^r sum_{i=1}^{n-k+1} sum_{j=i}^{i+k-1} A_j \ = & sum_{k=l}^r sum_{i=1}^{n-k+1} S_{i+k-1} - S_{i-1} \ = & sum_{k=l}^r (SS_n - SS_{k-1}) - (SS_{n-k} - 0) \ = & (r - l + 1) cdot SS_n - (SSS_{r-1} - SSS_{l-2}) - (SSS_{n-l} - SSS_{n-r-1}) end{aligned} ]

    这样我们就可以每次询问 (O(1)) 回答啦。

    接下来的任务就是对于每次修改,维护这次对于所有长度的增量。假设本次修改对长度 (x) 的增量为 (f(x)),那么容易发现 (f(x)) 是关于 (x) 的分段二次函数(为啥分段?因为要分类讨论各种左端点右端点在哪,是被修改区间包含还是有交还是包含修改区间等等等等,具体讨论过程我就不讲啦,参见代码吧);也就是说我们在线段树上的区间加标记变成了一个区间加二次函数,这个东西比较好处理,我们搞清楚起点(就是这个标记实际上是形如 (a(x+x_0)^2 + b(x+x_0) + c) 这样的,但是我们可以将其拆开变成 (a'x^2 + b'x + c') 的形式),并且加上这个标记之后线段树上维护的总和加上一个二次的自然数幂求和、一个公差为 (1) 的等差数列求和、一个常数即可。

    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 200010
    #define MOD 1000000007
    #define inv2 500000004
    #define inv6 166666668
    #define LL long long
    
    int A[maxn], S[maxn], SS[maxn], SSS[maxn];
    
    namespace Sgt {
    	int calc(int n) { return ((LL)n * (n + 1) >> 1) % MOD; }
    	int calc2(int x) { return (LL)inv6 * (x << 1 | 1) % MOD * (x + 1) % MOD * x % MOD; }
    	
    	struct Quadratic {
    		int a, b, c; // ax^2 + bx + c
    		Quadratic(): a(0), b(0), c(0) {}
    		Quadratic(int _1, int _2, int _3): a(_1), b(_2), c(_3) {}
    		
    		Quadratic operator + (const Quadratic& t) const {
    			int ta = a + t.a, tb = b + t.b, tc = c + t.c;
    			if(ta >= MOD) ta -= MOD;
    			if(tb >= MOD) tb -= MOD;
    			if(tc >= MOD) tc -= MOD;
    			return Quadratic(ta, tb, tc);
    		}
    		Quadratic operator += (const Quadratic& t) { return *this = *this + t; }
    		Quadratic operator * (const int& t) const { return Quadratic((LL)a * t % MOD, (LL)b * t % MOD, (LL)c * t % MOD); }
    		
    		Quadratic xplus(int x0) { // transform a(x + x0)^2 + b(x + x0) + c
    			return Quadratic(a, (2ll * a * x0 + b) % MOD, ((LL)a * x0 % MOD * x0 % MOD + (LL)b * x0 % MOD + c) % MOD);
    		}
    		
    		int sum(int n) { // calculate sum ax^2 + bx + c, where 1 <= x <= n
    			return ((LL)a * calc2(n) % MOD + (LL)b * calc(n) % MOD + (LL)c * n % MOD) % MOD;
    		}
    	} tag[maxn<<2];
    	int sumv[maxn<<2];
    	
    	void pushdown(int o, int l, int r) {
    		if(l == r || (!tag[o].a && !tag[o].b && !tag[o].c)) return (void)(tag[o] = Quadratic());
    		int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
    		Quadratic now = tag[o];
    		tag[lc] += now; sumv[lc] += now.sum(mid - l + 1); if(sumv[lc] >= MOD) sumv[lc] -= MOD;
    		now = tag[o].xplus(mid + 1 - l);
    		tag[rc] += now; sumv[rc] += now.sum(r - mid); if(sumv[rc] >= MOD) sumv[rc] -= MOD;
    		tag[o] = Quadratic();
    		return ;
    	}
    	void update(int o, int l, int r, int ql, int qr, Quadratic t) {
    		if(ql > qr) return ;
    		pushdown(o, l, r);
    		if(ql <= l && r <= qr) {
    			Quadratic now = t.xplus(l - 1);
    			tag[o] += now;
    			sumv[o] += now.sum(r - l + 1);
    			if(sumv[o] >= MOD) sumv[o] -= MOD;
    			return ;
    		}
    		int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
    		if(ql <= mid) update(lc, l, mid, ql, qr, t);
    		if(qr > mid) update(rc, mid + 1, r, ql, qr, t);
    		sumv[o] = sumv[lc] + sumv[rc];
    		if(sumv[o] >= MOD) sumv[o] -= MOD;
    		return ;
    	}
    	
    	int query(int o, int l, int r, int ql, int qr) {
    		if(ql > qr) return 0;
    		pushdown(o, l, r);
    		if(ql <= l && r <= qr) return sumv[o];
    		int mid = l + r >> 1, lc = o << 1, rc = lc | 1, ans = 0;
    		if(ql <= mid) {
    			ans += query(lc, l, mid, ql, qr);
    			if(ans >= MOD) ans -= MOD;
    		}
    		if(qr > mid) {
    			ans += query(rc, mid + 1, r, ql, qr);
    			if(ans >= MOD) ans -= MOD;
    		}
    		return ans;
    	}
    }
    
    int num[100], cntn;
    void putint(int x) {
    	if(!x) return (void)puts("0");
    	cntn = 0;
    	while(x) num[++cntn] = x % 10, x /= 10;
    	dwn(i, cntn, 1) putchar(num[i] + '0'); putchar('
    ');
    	return ;
    }
    
    int main() {
    	freopen("sum.in", "r", stdin);
    	freopen("sum.out", "w", stdout);
    	
    	int n = read(), q = read();
    	rep(i, 1, n) A[i] = read();
    	
    	rep(i, 1, n) {
    		S[i] = S[i-1] + A[i];
    		if(S[i] >= MOD) S[i] -= MOD;
    	}
    	rep(i, 1, n) {
    		SS[i] = SS[i-1] + S[i];
    		if(SS[i] >= MOD) SS[i] -= MOD;
    	}
    	rep(i, 1, n) {
    		SSS[i] = SSS[i-1] + SS[i];
    		if(SSS[i] >= MOD) SSS[i] -= MOD;
    	}
    	
    	using Sgt::Quadratic;
    	using Sgt::update;
    	using Sgt::query;
    	while(q--) {
    		int tp = read(), l = read(), r = read(), d;
    		if(l > r) swap(l, r);
    		if(tp == 1) {
    			d = read();
    			update(1, 1, n, 1, r - l + 1, Quadratic(MOD - 1, r - l + 2, 0) * d);
    			update(1, 1, n, 1, min(r - l + 1, l), Quadratic(inv2, MOD - inv2, 0) * d);
    			update(1, 1, n, l + 1, r - l + 1, Quadratic(0, l - 1, (LL)inv2 * (l - (LL)l * l % MOD + MOD) % MOD) * d);
    			update(1, 1, n, 1, min(r - l + 1, n - r + 1), Quadratic(inv2, MOD - inv2, 0) * d);
    			update(1, 1, n, n - r + 2, r - l + 1, Quadratic(0, n - r, (LL)inv2 * (r - n - 1 + MOD) % MOD * (n - r) % MOD) * d);
    			
    			update(1, 1, n, r - l + 2, l, Quadratic(0, 0, ((LL)(r - l) * (r - l + 1) >> 1) % MOD) * d);
    			update(1, 1, n, max(r - l + 2, l + 1), r - 1, Quadratic(MOD - inv2, (LL)inv2 * (2ll * l - 1 + MOD) % MOD, (LL)inv2 * r % MOD * (r - 2ll * l + 1 + MOD) % MOD) * d);
    			update(1, 1, n, r - l + 2, n - r + 1, Quadratic(0, 0, ((LL)(r - l) * (r - l + 1) >> 1) % MOD) * d);
    			update(1, 1, n, max(r - l + 2, n - r + 2), n - l + 1, Quadratic(MOD - inv2, (LL)inv2 * ((n << 1) - (r << 1) + 1) % MOD, (LL)inv2 * (2ll * r - n - l + MOD) % MOD * (n - l + 1) % MOD) * d);
    			if(r < n - l + 1) {
    				update(1, 1, n, r - l + 2, r, Quadratic(0, r - l + 1, (LL)(r - l + 1) * (l - r + MOD) % MOD) * d);
    				update(1, 1, n, max(r - l + 2, r + 1), n - l, Quadratic(0, 0, (LL)l * (r - l + 1) % MOD) * d);
    				update(1, 1, n, max(r - l + 2, n - l + 1), n, Quadratic(0, MOD - (r - l + 1), (LL)(n + 1) * (r - l + 1) % MOD) * d);
    			}
    			else {
    				update(1, 1, n, r - l + 2, n - l, Quadratic(0, r - l + 1, (LL)(r - l + 1) * (l - r + MOD) % MOD) * d);
    				update(1, 1, n, max(r - l + 2, n - l + 1), r, Quadratic(0, 0, (LL)(n - r + 1) * (r - l + 1) % MOD) * d);
    				update(1, 1, n, max(r - l + 2, r + 1), n, Quadratic(0, MOD - (r - l + 1), (LL)(n + 1) * (r - l + 1) % MOD) * d);
    			}
    		}
    		else {
    			int ans = (LL)(r - l + 1) * SS[n] % MOD;
    			ans -= (SSS[r-1] - (l - 2 >= 0 ? SSS[l-2] : 0) + MOD) % MOD; if(ans < 0) ans += MOD;
    			ans -= (SSS[n-l] - (n - r - 1 >= 0 ? SSS[n-r-1] : 0) + MOD) % MOD; if(ans < 0) ans += MOD;
    			putint((ans + query(1, 1, n, l, r)) % MOD);
    		}
    	}
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    

    治疗之雨

    试题描述

    (没玩过《炉石传说》的人可以跳过这一段)今天我们来探讨下《炉石传说》中“治疗之雨”(恢复 (12) 点生命值,随机分配到所有友方角色上)和“暗影打击装甲”(每当一个角色获得治疗,便对随机敌人造成 (1) 点伤害)这两张卡牌之间的互动效果。假设你场上有 (m) 个剩余生命值无限大且生命值上限减去剩余生命值也无限大的随从,而对方的场上有 (k) 个暗影打击装甲,你的英雄剩余生命值为 (p) 、生命值上限为 (n),现在你使用了一张可以恢复无限多(而不是 (12) 点)生命值的治疗之雨,问治疗之雨期望总共恢复了几点生命值以后你的英雄会死亡(生命值降为 (0) ;治疗之雨的判定机制使得在此后再也不会为英雄恢复生命值)。

    注:题目背景与题目描述有冲突的地方请以题目描述为准

    下面让我们再形式化地描述一遍问题。

    你现在有 (m+1) 个数:第一个为 (p),最小值为 (0),最大值为 (n);剩下 (m) 个都是无穷,没有最小值或最大值。你可以进行任意多轮操作,每轮操作如下:

    在不为最大值的数中等概率随机选择一个(如果没有则不操作),把它加一;

    进行 (k) 次这个步骤:在不为最小值的数中等概率随机选择一个(如果没有则不操作),把它减一。

    现在问期望进行多少轮操作以后第一个数会变为最小值 (0)

    输入

    输入包含多组数据。

    输入第一行包含一个正整数 (T),表示数据组数。

    接下来 (T) 行 ,每行 (4) 个非负整数 (n)(p)(m)(k)(含义见题目描述),表示一次询问。

    输出

    输出 (T) 行,每行一个整数,表示一次询问的答案。

    如果无论进行多少轮操作,第一个数都不会变为最小值 (0) ,那么输出 (-1)

    否则,可以证明答案一定为有理数,那么请输出答案模 (1000000007) 的余数,即设答案为 (frac{a}{b}()a(、)b$ 为互质的正整数 ),你输出的整数为 (x),那么你需要保证 (0 le x < 1000000007)(a equiv bx (mod 1000000007))

    输入示例

    2
    2 1 1 1
    2 2 1 1
    

    输出示例

    6
    8
    

    数据规模及约定

    对于 (10\%) 的数据,(n le 3)(m, k le 2)

    对于 (20\%) 的数据,(n, m, k le 5)

    对于 (30\%) 的数据,(n, m, k le 30)

    对于 (40\%) 的数据,(n, m, k le 50)

    对于 (50\%) 的数据,(n, m, k le 200)

    对于 (70\%) 的数据,(n le 200)

    对于 (80\%) 的数据,(n le 500)

    对于 (100\%) 的数据,(1 le T le 100)(1 le p le n le 1500)(0 le m, k le 1000000000)

    //保证不存在 (n=p=k=1)(m=0) 的情况(因为出题人判错了)

    //保证不存在答案的分母是 (1000000007) 的倍数的情况(因为出题人没想到)

    题解

    这种题一般都是列方程。令 (f(p)) 表示还剩 (p) 的血量,血量掉成 (0) 的期望轮数。

    那么我们可以列出如下方程:

    [egin{aligned} egin{cases} f(p) & = f(p+1) cdot frac{1}{m+1} cdot left( frac{m}{m+1} ight)^k + sum_{i=0}^{min { p - 1, k }} f(p - i) cdot left[ frac{1}{m+1} cdot left( frac{1}{m+1} ight)^{i+1} cdot left( frac{m}{m+1} ight)^{k-i-1} cdot {k choose i + 1} + frac{m}{m+1} cdot left( frac{1}{m+1} ight)^i cdot left( frac{m}{m+1} ight)^{k-i} cdot {k choose i} ight] && p < n \ f(p) & = sum_{i=0}^{min { p - 1, k }} f(p - i) cdot left( frac{1}{m+1} ight)^i cdot left( frac{m}{m+1} ight)^{k-i} cdot {k choose i} && p = n end{cases} end{aligned} ]

    这个方程就是考虑第一次是否奶中,然后枚举从 (f(p - i)) 那里转移过来,这时就必须恰好扣掉 (i) 滴血。由于 (f(0) = 0),所以 (f(0)) 的复杂的概率可以直接不考虑。由于它不可能奶中满血的英雄,所以 (f(n)) 需要单独讨论。

    考虑高斯消元,由于这个矩阵是长得很像对角矩阵,所以高斯消元能优化到 (O(n^2))

    但是我的做法是将所有的 (f(p) (1 < p le n)) 表示成关于 (f(1)) 的一次函数,然后对于方程的最后一条我们可以得到额外的一个 (f(n)) 关于 (f(1)) 的一次函数,由此联立解出 (f(1)),从而得到 (f(p))(过程应该是本质相同的)。

    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 1510
    #define MOD 1000000007
    #define LL long long
    
    int n, P, m, k;
    int ifac[maxn], suf[maxn], pw1[maxn], pwm[maxn], iv, ivm;
    /*
    pw1[i] means [1/(m+1)]^i, pwm[i] means [m/(m+1)]^(k-i)
    fac[i] means 1*2*...*i, suf[i] means (k-i+1)*(k-i+2)*...*k
    i in [0, n]
    iv means 1/(m+1), ivm means m/(m+1)
    */
    
    int Pow(int a, int b) {
    	if(b < 0) return 0;
    	int ans = 1, t = a;
    	while(b) {
    		if(b & 1) ans = (LL)ans * t % MOD;
    		t = (LL)t * t % MOD; b >>= 1;
    	}
    	return ans;
    }
    
    void init() {
    	ifac[0] = 1;
    	rep(i, 1, n) ifac[i] = (LL)ifac[i-1] * Pow(i, MOD - 2) % MOD;
    	suf[0] = 1;
    	rep(i, 1, n) suf[i] = (LL)suf[i-1] * (k - i + 1 + MOD) % MOD;
    	iv = Pow(m + 1, MOD - 2); ivm = (LL)iv * m % MOD;
    	rep(i, 0, n) pw1[i] = Pow(iv, i), pwm[i] = Pow(ivm, k - i);
    	return ;
    }
    
    LL C(int x) { // calculate choose(k, x)
    	if(x > k) return 0;
    	return (LL)suf[x] * ifac[x] % MOD;
    }
    
    int A[maxn], B[maxn];
    void work() {
    	n = read(); P = read(); m = read(); k = read();
    	if(!m) {
    		if(k <= 1) return (void)puts("-1");
    		int cnt = 0;
    		if(P == n) P -= k, cnt++;
    		if(P <= 0) return (void)printf("%d
    ", cnt);
    		cnt += (P + k - 2) / (k - 1);
    		return (void)printf("%d
    ", cnt);
    	}
    	if(k == 0) return (void)puts("-1");
    	
    	init();
    	
    //	rep(i, 0, n) printf("%d%c", pw1[i], i < n ? ' ' : '
    ');
    //	rep(i, 0, n) printf("%d%c", pwm[i], i < n ? ' ' : '
    ');
    	
    	A[1] = 1; B[1] = 0;
    	rep(p, 1, n - 1) {
    		int rate = MOD - (LL)iv * pwm[0] % MOD, a = MOD - A[p], b = MOD - B[p] + 1;
    		if(rate >= MOD) rate -= MOD; if(a >= MOD) a -= MOD; if(b >= MOD) b -= MOD;
    		rep(i, 0, min(p - 1, k)) {
    			int nr = (i + 1 <= k) ? ((LL)iv * pw1[i+1] % MOD * pwm[i+1] % MOD * C(i + 1) % MOD) : 0;
    //			printf("%d * %d * %d * %d
    ", iv, pw1[i+1], pwm[i+1], C(i + 1));
    			nr += (LL)ivm * pw1[i] % MOD * pwm[i] % MOD * C(i) % MOD;
    //			printf("%d: %d  %d %lld + %lld
    ", p, p - i, nr, (LL)iv * pw1[i+1] % MOD * pwm[i+1] % MOD * C(i + 1) % MOD, (LL)ivm * pw1[i] % MOD * pwm[i] % MOD * C(i) % MOD);
    			if(nr >= MOD) nr -= MOD;
    			a += (LL)nr * A[p-i] % MOD; b += (LL)nr * B[p-i] % MOD;
    			if(a >= MOD) a -= MOD; if(b >= MOD) b -= MOD;
    		}
    //		printf("rate: %d  %d %d
    ", Pow(rate, MOD - 2), a, b);
    		A[p+1] = (LL)Pow(rate, MOD - 2) * a % MOD; B[p+1] = (LL)Pow(rate, MOD - 2) * b % MOD;
    	}
    	int f1, a = 0, b = 1, rate = 1;
    	rep(i, 0, min(n - 1, k)) {
    		int nr = (LL)pw1[i] * pwm[i] % MOD * C(i) % MOD;
    		if(!i) {
    			rate = rate + MOD - nr;
    			if(rate >= MOD) rate -= MOD;
    		}
    		else {
    			a += (LL)nr * A[n-i] % MOD; b += (LL)nr * B[n-i] % MOD;
    			if(a >= MOD) a -= MOD; if(b >= MOD) b -= MOD;
    		}
    	}
    	a = (LL)a * Pow(rate, MOD - 2) % MOD; b = (LL)b * Pow(rate, MOD - 2) % MOD;
    	f1 = (LL)(b - B[n] + MOD) * Pow((A[n] - a + MOD) % MOD, MOD - 2) % MOD;
    //	rep(i, 1, n) printf("%d: %dx + %d
    ", i, A[i], B[i]);
    //	printf("%dx + %d = %dx + %d
    ", A[n], B[n], a, b);
    	printf("%lld
    ", ((LL)A[P] * f1 + B[P]) % MOD);
    	return ;
    }
    
    int main() {
    	freopen("heal.in", "r", stdin);
    	freopen("heal.out", "w", stdout);
    	
    	int T = read();
    	while(T--) work();
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    
  • 相关阅读:
    数据库查询服务DBCacheServer
    SmallMQ实现发布
    数据库查询服务DBCacheServer
    Mysql ACID与隔离级别
    jsp拾遗
    Git项目创建与提交
    Spring Boot详细学习地址转载
    Spring Cloud微服务体系搭建
    前后端分离项目启动参考
    JVM类加载机制总结
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8882008.html
Copyright © 2011-2022 走看看