首页 > 其他分享 >省选联考 2022 填树

省选联考 2022 填树

时间:2023-10-28 13:44:06浏览次数:42  
标签:省选 res ll poly int 填树 maxn 联考 mod

洛谷传送门

LOJ 传送门

这题做得真艰难。

先考虑第一问。

一眼看上去并没有什么复杂度脱离值域的办法。考虑枚举一个 \(x\) 表示最小值,那么点权只能在 \([x, x + K]\) 中。

点权最小值不一定为 \(x\),减去点权在 \([x + 1, x + K]\) 中的答案即可,也就是把 \(K\) 减 \(1\) 后再算一遍。

那么可以得出每个点权的取值范围为 \([\max(x, l_i), \min(x + K, r_i)]\)。

设第 \(i\) 个点有 \(a_u\) 种取值。答案就是树上所有简单路径的 \(a_u\) 乘积之和。

那么很容易做一个 dp,可以算出 \(f_u\) 表示 \(u\) 子树内延伸到 \(u\) 的路径中,每条路径的取值之和。

合并儿子时有 \(f_u \gets a_u \times f_v\)。

然后考虑所有 \(\text{LCA}\) 为 \(u\) 的点对的贡献,相当于选 \(v_1 \in son_u, v_2 \in son_u, v1 \ne v2\),能产生 \(f_{v_1} \times f_{v_2} \times a_u\) 的贡献。

我们发现,\([\max(x, l_i), \min(x + K, r_i)]\) 只能组合出来 \(4\) 种取值范围:\([x, x + K], [x, r_i], [l_i, x + K], [l_i, r_i]\),并且只和 \(x\) 和 \(l_i - K, l_i, r_i - K, r_i\) 的大小关系有关。

所以我们有 \(O(n)\) 个断点,在每相邻两个断点组成的左开右闭区间 \([L, R)\) 内,\(a_u\) 可以表示成关于 \(x\) 的至多一次项式 \(Ax + B\)。

我们现在希望计算树上所有简单路径的多项式 \(a_u\) 的乘积之和,可以使用上述的 dp 做法求出,多项式乘法暴力就行。

设我们最后求出来的树上所有简单路径的多项式 \(a_u\) 的乘积之和为 \(\sum\limits_{i = 0}^n A_i x^i\)。答案就是 \(\sum\limits_{i = 0}^n A_i \sum\limits_{x = L}^{R - 1} x^i\)。也就是说要快速算 \(\sum\limits_{x = 0}^N x^M\)。

这就是 CF622F The Sum of the k-th Powers。这个和就是一个 \(M + 1\) 次多项式,直接拉格朗日插值即可。注意讨论 \(L, R\) 中有负数的情况。

然后考虑第二问。

仍然先考虑暴力。设第 \(u\) 个点所有取值之和为 \(b_u\),那么对于树上一条简单路径 \(p_1, p_2, \ldots, p_k\),我们希望求 \(\sum\limits_{i = 1}^k b_{p_i} \prod\limits_{j \ne i} a_{p_j}\)。

这个也可以 dp 求出。设 \(f_{u, 0/1}\) 表示一条从 \(u\) 子树内延伸到 \(u\) 的路径,中间是否有一个点乘的是 \(b_i\) 而不是 \(a_i\)。

合并儿子时有转移 \(f_{u, 1} \gets a_u f_{v, 1} + b_u f_{v, 0}\)。

然后仍然考虑所有 \(\text{LCA}\) 为 \(u\) 的点对的贡献,相当于选 \(v_1 \in son_u, v_2 \in son_u, v1 \ne v2\),能产生 \(f_{v_1, 0} \times f_{v_2, 0} \times b_u + f_{v_1, 1} \times f_{v_2, 0} \times a_u + f_{v_1, 0} \times f_{v_2, 1} \times a_u\) 的贡献。

然后也可以像第一问一样,先分段,然后把 \(b_u\) 表示成关于 \(x\) 的至多二次项式,dp 后拉格朗日插值算 \(\sum\limits_{x = 0}^N x^M\) 解决。

时间复杂度 \(O(n^3)\),但是好像跑得比大多数做法都快?

code
// Problem: P8290 [省选联考 2022] 填树
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P8290
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 2020;
const ll mod = 1000000007;
const ll inv2 = (mod + 1) / 2;

inline ll qpow(ll b, ll p) {
	ll res = 1;
	while (p) {
		if (p & 1) {
			res = res * b % mod;
		}
		b = b * b % mod;
		p >>= 1;
	}
	return res;
}

ll n, m, a[maxn], b[maxn], lsh[maxn], tot, pw[maxn][maxn], fac[maxn], ifac[maxn];
vector<int> G[maxn];

typedef vector<ll> poly;

inline poly operator + (const poly &a, const poly &b) {
	int n = (int)a.size(), m = (int)b.size();
	poly res(max(n, m));
	for (int i = 0; i < max(n, m); ++i) {
		if (i < n) {
			res[i] += a[i];
		}
		if (i < m) {
			res[i] += b[i];
		}
		(res[i] >= mod) && (res[i] -= mod);
	}
	return res;
}

inline poly operator * (const poly &a, const poly &b) {
	if (a.empty() || b.empty()) {
		return poly();
	}
	int n = (int)a.size() - 1, m = (int)b.size() - 1;
	poly res(n + m + 1);
	for (int i = 0; i <= n; ++i) {
		for (int j = 0; j <= m; ++j) {
			res[i + j] = (res[i + j] + a[i] * b[j]) % mod;
		}
	}
	return res;
}

ll pre[maxn], suf[maxn];

// 0 ^ m + 1 ^ m + 2 ^ m + ... + n ^ m
inline ll calc(ll n, ll m) {
	if (n <= 0) {
		return 0;
	}
	if (n <= m + 5) {
		ll ans = 0;
		for (int i = 0; i <= n; ++i) {
			ans = (ans + pw[i][m]) % mod;
		}
		return ans;
	}
	pre[0] = 1;
	for (int i = 1; i <= m + 2; ++i) {
		pre[i] = pre[i - 1] * (n - i) % mod;
	}
	suf[m + 3] = 1;
	for (int i = m + 2; i; --i) {
		suf[i] = suf[i + 1] * (n - i) % mod;
	}
	ll s = 0, ans = 0;
	for (int i = 1; i <= m + 2; ++i) {
		s = (s + pw[i][m]) % mod;
		ll coef = pre[i - 1] * suf[i + 1] % mod;
		coef = coef * ifac[i - 1] % mod * ifac[m + 2 - i] % mod;
		if ((m + 2 - i) & 1) {
			coef = (mod - coef) % mod;
		}
		ans = (ans + coef * s) % mod;
	}
	return ans;
}

// l ^ m + (l + 1) ^ m + (l + 2) ^ m + ... + r ^ m
inline ll calc(ll l, ll r, ll m) {
	if (!m) {
		return (r - l + 1) % mod;
	}
	if (l <= 0 && r <= 0) {
		return (mod + ((m & 1) ? (-calc(-l, m) + calc(-r - 1, m)) : (calc(-l, m) - calc(-r - 1, m)))) % mod;
	} else if (l <= 0 && r > 0) {
		return (mod + mod + ((m & 1) ? -calc(-l, m) : calc(-l, m)) + calc(r, m)) % mod;
	} else {
		return (calc(r, m) - calc(l - 1, m) + mod) % mod;
	}
}

poly A[maxn], B[maxn], P, Q, F[maxn][2];

void dfs(int u, int fa) {
	F[u][0] = A[u];
	F[u][1] = B[u];
	for (int v : G[u]) {
		if (v == fa) {
			continue;
		}
		dfs(v, u);
		F[u][0] = F[u][0] + A[u] * F[v][0];
		F[u][1] = F[u][1] + A[u] * F[v][1] + B[u] * F[v][0];
	}
	P = P + F[u][0];
	Q = Q + F[u][1];
}

void dfs2(int u, int fa) {
	poly a, b;
	for (int v : G[u]) {
		if (v == fa) {
			continue;
		}
		dfs2(v, u);
		P = P + F[v][0] * A[u] * a;
		Q = Q + F[v][0] * A[u] * b + F[v][1] * A[u] * a + F[v][0] * B[u] * a;
		a = a + F[v][0];
		b = b + F[v][1];
	}
}

inline pii calc(ll m) {
	tot = 0;
	for (int i = 1; i <= n; ++i) {
		lsh[++tot] = a[i];
		lsh[++tot] = a[i] - m;
		lsh[++tot] = b[i] - m;
		lsh[++tot] = b[i] + 1;
	}
	sort(lsh + 1, lsh + tot + 1);
	tot = unique(lsh + 1, lsh + tot + 1) - lsh - 1;
	ll ans1 = 0, ans2 = 0;
	for (int _ = 1; _ < tot; ++_) {
		ll L = lsh[_], R = lsh[_ + 1];
		for (int i = 1; i <= n; ++i) {
			A[i] = B[i] = poly();
			ll l = a[i], r = b[i];
			if (max(l, L) > min(r, L + m)) {
				continue;
			}
			if (L >= l && L >= r - m) {
				A[i] = poly(2);
				A[i][0] = r + 1;
				A[i][1] = mod - 1;
				B[i] = poly(3);
				B[i][0] = (r * r + r) % mod * inv2 % mod;
				B[i][1] = inv2;
				B[i][2] = (mod - inv2) % mod;
			} else if (L >= l && L < r - m) {
				A[i] = poly(1);
				A[i][0] = m + 1;
				B[i] = poly(2);
				B[i][0] = m * (m + 1) % mod * inv2 % mod;
				B[i][1] = m + 1;
			} else if (L < l && L >= r - m) {
				A[i] = poly(1);
				A[i][0] = r - l + 1;
				B[i] = poly(1);
				B[i][0] = calc(l, r, 1);
			} else if (L < l && L < r - m) {
				A[i] = poly(2);
				A[i][0] = (m - l + 1 + mod) % mod;
				A[i][1] = 1;
				B[i] = poly(3);
				ll p = (l + m) % mod, q = (m - l + 1 + mod) % mod;
				B[i][0] = p * q % mod * inv2 % mod;
				B[i][1] = inv2 * (p + q) % mod;
				B[i][2] = inv2;
			}
		}
		P = Q = poly();
		dfs(1, -1);
		dfs2(1, -1);
		for (int i = 0; i < (int)P.size(); ++i) {
			ans1 = (ans1 + P[i] * calc(L, R - 1, i)) % mod;
		}
		for (int i = 0; i < (int)Q.size(); ++i) {
			ans2 = (ans2 + Q[i] * calc(L, R - 1, i)) % mod;
		}
		// printf("%lld %lld %lld\n", L, R, ans2);
		// for (int i = 1; i <= n; ++i) {
			// printf("i: %d\n", i);
			// for (ll x : B[i]) {
				// printf("%lld ", x);
			// }
			// putchar('\n');
		// }
	}
	return mkp(ans1, ans2);
}

void solve() {
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld%lld", &a[i], &b[i]);
	}
	int up = n * 2 + 5;
	for (int i = 0; i <= up; ++i) {
		pw[i][0] = 1;
		for (int j = 1; j <= up; ++j) {
			pw[i][j] = pw[i][j - 1] * i % mod;
		}
	}
	fac[0] = 1;
	for (int i = 1; i <= up; ++i) {
		fac[i] = fac[i - 1] * i % mod;
	}
	ifac[up] = qpow(fac[up], mod - 2);
	for (int i = up - 1; ~i; --i) {
		ifac[i] = ifac[i + 1] * (i + 1) % mod;
	}
	for (int i = 1, u, v; i < n; ++i) {
		scanf("%d%d", &u, &v);
		G[u].pb(v);
		G[v].pb(u);
	}
	pii x = calc(m), y = calc(m - 1);
	printf("%lld\n%lld\n", (x.fst - y.fst + mod) % mod, (x.scd - y.scd + mod) % mod);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:省选,res,ll,poly,int,填树,maxn,联考,mod
From: https://www.cnblogs.com/zltzlt-blog/p/17794019.html

相关文章

  • P5289 [十二省联考 2019] 皮配
    很容易想到设\(dp_{i,j,k}\)表示考虑前\(i\)个阵营,\(C_0=j\),\(D_0=k\)时的方案数,层内转移时可以用辅助数组对两种阵营决策分别转移,此时时间复杂度为\(O(nM^2)\)。考虑\(k=0\)的情况,如果我们能做这个的话,\(k=30\)其实就是在暗示我们把特殊选手拆出来单独做。而如果所有选......
  • 【多校联考NOIP#3】比赛复盘 && 题解
    A.卡牌这次比赛,一道签到题都没有。本来以为是线段树上二分。就类似于花神的数论题那道,刚开始暴力修改(修改到线段树的每一个叶子节点),然后由于boss的attack在不断增加,到了\(Att_i>=hp_j\)的时候,\(j\)这个牌顶多打一次,如果一个区间的\(max\)都小于boss的攻击力了,那么就不......
  • 八点五省联考 2018
    一双木棋状态数不多,直接爆搜https://loj.ac/s/1676274IIIDX考虑依次给\(i=1,2,\cdots,n\)填上数,每次尽量填最大的。考虑什么时候\(i\)填上\(x\)是合法的。考虑Hall定理,发现左部点约束最严的时候肯定是找一个已经填过的点\(u\),然后对所有\(d_v\ged_u\)的\(v\),选出......
  • NOI2024省选训练赛 11 解题报告
    NOI2024省选训练赛11解题报告目录NOI2024省选训练赛11解题报告A.小L的栈DescriptionConstraintsSolutionConclusionB.intervalDescriptionConstraintsSolutionConclusionC.DigitSumDescriptionConstraintsSolutionConclusionD.机器故障探测DescriptionConstraintsSoluti......
  • 联考test1009
    写在前面的话感觉比以往的比赛难多了。出题人卡高精度,不好评价,但是题目还是好题。考试的时候开题顺序为\(T1-T3-T4-T2\),感觉和题目的实际难度排序差不多。考试的时候懒了,没有去拼暴力,实际得分\(80+0+100+0=180\),总体排名\(rk29\)。\(T1\)题意简述我们知道,对于一个整......
  • P9546 [湖北省选模拟 2023] 山路长环 / ring
    Day\(\mathbb{P}_9\)。如果有权值为\(0\)的边,用所有这样的边把环分成若干条链。不难发现若起始点在链的一端,先手必胜当且仅当链长(边数)为奇数。可以进行归纳证明,这种情况下先手每次移动必将边权置为\(0\)。继续推性质:起始点在长度为奇数的链(奇链)上,那么删掉这个点后,这条链......
  • 2023.09.26 联考总结&题解
    T1derby你考虑直接贪心进行匹配即可,就是说对于每一个\(1\)去匹配最大的\(0\)#include<bits/stdc++.h>usingnamespacestd;intn,m;vector<int>A[2],B[2];intmain(){ freopen("derby.in","r",stdin); freopen("derby.out","w",s......
  • 2023LN省选游记
    前言CSP第一轮都考完了,我才写这个游记。我真懒惰书接上回正文Day-114514我也没想到我居然能报省选。报上了。准备去爆零。Day-114513~Day-1学习暴力算法以及痛苦的whkDay0放学后带着书包直接润开发区住酒店。不然我担心我起不来。晚上的轻轨真好,虽然人依然不少。D......
  • P9545 [湖北省选模拟 2023] 环山危路 / road
    题意就是给定一个竞赛图,多次询问,每次询问有多个源点\(s_1,s_2,\cdotss_k\),单个汇点\(t\),一条边流量为\(1\),求最大流。考虑转成最小割,相当于将\(V\)划分成两个集合\(S,T\),\(S\cupT=V\)且\(S\capT=\varnothing\),\(s_i\inS,t\inT\),然后令\(f(S,T)=\sum\limits_{u\inS......
  • NOI2024省选训练赛01
    NOI2024省选训练赛01时间:2023.9.16目录NOI2024省选训练赛01A.t3DescriptionConstraintsSolutionB.LifeDescriptionConstraintsSolutionA.t3TimeLimit:4sec/MemoryLimit:512MBDescription维护一个长度为\(n\)的数列\(a_i\),支持如下几种操作,操作有\(m\)次。\(1......