hdu 多校 2019 选做

orz sqc


hdu6701 多校2019Day10K Make Rounddog Happy

题目传送门

https://vjudge.net/problem/HDU-6701

(vjudge 重度依赖,原谅我不尊重原题哈)

题解

我们很轻松地求出一个最大值管辖哪些区域,但是由于“所选区间每个元素要互不相同”这一限制条件的存在,所以不能直接求出。最优的方法需要枚举一个端点,然后通过预处理 \(pre_i\) 表示 \(i\) 位置上的数之前的最后一次出现的位置,那么这个东西的前缀最大值就是可行以 \(i\) 为右端点时的极限左端点。

但是很显然这样做是 \(O(n^2)\) 的。

可以发现,每次如果只处理跨过最大值的区间,然后递归剩下的区间,就可以做出来了。但是每一次需要扫左右两边的其中一个区间。

所以可以用启发式分裂(这样叫是因为最后反过来看这就是一个启发式合并),每次扫描较小区间。


时间复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 300000 + 7;
const int LOG = 19;

int n, k;
ll ans;
int a[N], pre[N], vpre[N], nxt[N], vnxt[N];
pii rmq[N][LOG];

inline void rmq_init() {
	for (int i = 1; i <= n; ++i) rmq[i][0] = pii(a[i], i);
	for (int j = 1; (1 << j) <= n; ++j)
		for (int i = 1; i + (1 << j) - 1 <= n; ++i)
			rmq[i][j] = std::max(rmq[i][j - 1], rmq[i + (1 << (j - 1))][j - 1]);
}
inline pii qmax(int l, int r) {
	int k = std::__lg(r - l + 1);
	return std::max(rmq[l][k], rmq[r - (1 << k) + 1][k]);
}

inline void solve(int l, int r) {
	if (l > r) return;
	int mid = qmax(l, r).se;
	if (mid - l <= r - mid) {
		int mn, nd = a[mid] - k;
		for (int i = mid; i >= l; --i) {
			mn = std::min(nxt[i], r);
			if (mn < mid) break;
			if (mn - i + 1 >= nd) ans += mn - std::max(mid, i + nd - 1) + 1;
		}
	} else {
		int mx = 0, nd = a[mid] - k;
		for (int i = mid; i <= r; ++i) {
			mx = std::max(pre[i], l);
			if (mx > mid) break;
			if (i - mx + 1 >= nd) ans += std::min(mid, i - nd + 1) - mx + 1;
		}
	}
	solve(l, mid - 1), solve(mid + 1, r);
}

inline void work() {
	rmq_init();
	ans = 0;
	solve(1, n);
	printf("%lld\n", ans);
}

inline void init() {
	read(n), read(k);
	memset(vpre, 0, sizeof(int) * (n + 1));
	for (int i = 1; i <= n; ++i) {
		read(a[i]);
		pre[i] = std::max(pre[i - 1], vpre[a[i]] + 1);
		vpre[a[i]] = i;
	}
	for (int i = 1; i <= n; ++i) vnxt[i] = n + 1;
	nxt[n + 1] = n + 1;
	for (int i = n; i; --i) nxt[i] = std::min(nxt[i + 1], vnxt[a[i]] - 1), vnxt[a[i]] = i;
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	int T;
	read(T);
	while (T--) {
		init();
		work();
	}
	fclose(stdin), fclose(stdout);
	return 0;
}

hdu6643 多校2019Day6J Ridiculous Netizens

题目传送门

https://vjudge.net/problem/HDU-6643

题解

其实还是很妙的。

首先点分治+dfs序以后转化为序列背包是一个经典套路了,dfs 序上如果选这个点直接从前一个转移过来,如果不选就跳过自己的子树来转移。

主要是这个背包应该怎么做。

如果直接令 \(dp[i][j]\) 表示前 \(i\) 个数构成的乘积为 \(j\) 的方案数的话,时间复杂度为 \(O(nm)\) 还有点分治的 \(\log\) 无法接受。

不过可以发现,题目要求的只是乘上一些数以后 \(\leq m\),我们只关心一个数后面会不会乘到 \(m\) 以上。

所以,如果 \(\lfloor\frac ma\rfloor = \lfloor\frac mb\rfloor\) 的话,其实 \(a\)\(b\) 是没有区别的,因为只要这个东西一样,后面要乘到大于 \(m\) 所需要的乘积也是一样的。

所以可以不妨考虑成一开始有一个 \(m\),问多少方案可以把 \(m\) 除到不为 \(0\)

这样就可以设 \(dp[i][j]\) 表示现在 \(m\) 已经被除乘了 \(j\) 的方案数。\(j\) 一定是 \(m\) 的约数,所以 \(j\) 只有 \(O(\sqrt m)\) 个。

这样的总时间复杂度为 \(O(n\sqrt m\log n)\)


#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 2000 + 7;
const int M = 1e6 + 7;
const int P = 1e9 + 7;

int n, m, cnt, sum, mima, rt, dfc, ans;
int a[N], v[M], p[N], siz[N], vis[N], pre[N], dfn[N], dp[N][N];

struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }

inline int smod(int x) { return x >= P ? x - P : x; }
inline void sadd(int &x, const int &y) { x += y; x >= P ? x -= P : x; }
inline int fpow(int x, int y) {
	int ans = 1;
	for (; y; y >>= 1, x = (ll)x * x % P) if (y & 1) ans = (ll)ans * x % P;
	return ans;
}

inline void getrt(int x, int fa = 0) {
	int f = 0; siz[x] = 1;
	for fec(i, x, y) if (y != fa && !vis[y]) getrt(y, x), siz[x] += siz[y], smax(f, siz[y]);
	smax(f, sum - siz[x]);
	if (smin(mima, f)) rt = x;
}
inline void dfs(int x, int fa = 0){
	siz[x] = 1;
	for fec(i, x, y) if (y != fa && !vis[y]) dfs(y, x), siz[x] += siz[y];
	dfn[x] = ++dfc, pre[dfc] = x;
}
inline void calc(int x) {
	dfc = 0, dfs(x);
	dp[0][v[m]] = 1;
	for (int i = 1; i <= dfc; ++i) {
		memset(dp[i], 0, sizeof(int) * (cnt + 1));
		for (int j = 1; j <= cnt; ++j)
			if (p[j] / a[pre[i]]) sadd(dp[i][v[p[j] / a[pre[i]]]], dp[i - 1][j]);
		for (int j = 1; j <= cnt; ++j) sadd(dp[i][j], dp[i - siz[pre[i]]][j]);
	}
	for (int i = 1; i <= cnt; ++i) sadd(ans, dp[dfc][i]);
	sadd(ans, P - 1);
}
inline void solve(int x) {
	vis[x] = 1, calc(x);
	for fec(i, x, y) if (!vis[y]) {
		mima = sum = siz[y], getrt(y);
		solve(rt);
	}
}

inline void ycl() {
	for (int i = 1; i <= m; ++i)
		if (i == 1 || m / (i - 1) != m / i) p[++cnt] = m / i, v[m / i] = cnt;
}

inline void work() {
	ycl();
	sum = mima = n, getrt(1);
	solve(rt);
	printf("%d\n", ans);
}

inline void cls() {
	cnt = tot = ans = 0;
	memset(head, 0, sizeof(int) * (n + 1));
	memset(vis, 0, sizeof(int) * (n + 1));
}

inline void init() {
	read(n), read(m);
	cls();
	for (int i = 1; i <= n; ++i) read(a[i]);
	int x, y;
	for (int i = 1; i < n; ++i) read(x), read(y), adde(x, y);
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	int T;
	read(T);
	while (T--) {
		init();
		work();
	}
	fclose(stdin), fclose(stdout);
	return 0;
}

hdu6635 多校2019Day6B Nonsense Time

题目传送门

https://vjudge.net/problem/HDU-6635

题解

既然题目保证排列是完全随机的,那么这个排列的 LIS 在期望情况下是 \(O(\sqrt n)\) 的。

加点不太好考虑,我们不妨反过来删点。

删去一个点时候,如果这个点不在之前的 LIS 中,那么 LIS 长度显然不会改变。否则,我们暴力重构一遍 LIS。

因为 LIS 的期望长度是 \(O(\sqrt n)\),所以一个点在 LIS 中的概率也就是 \(\sqrt n\)。所以期望被暴力重构 \(\sqrt n\) 次。

所以在期望情况下,这个算法的时间复杂度为 \(O(n\sqrt n \log n)\)


#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 50000 + 7;

#define lowbit(x) ((x) & -(x))

int n, ansl;
int a[N], t[N], tp[N], dp[N], pre[N], v[N], ans[N];

pii s[N];
inline void qadd(int x, pii k) {
	for (; x <= n; x += lowbit(x))
		smax(s[x], k);
}
inline pii qmax(int x) {
	pii ans(0, 0);
	for (; x; x -= lowbit(x)) smax(ans, s[x]);
	return ans;
}

inline void rebuild(int ti) {
	memset(s, 0, sizeof(pii) * (n + 1));
	memset(v, 0, sizeof(int) * (n + 1));
	pii ans(0, 0);
	for (int i = 1; i <= n; ++i) if (t[i] < ti) pre[i] = qmax(a[i]).se, dp[i] = dp[pre[i]] + 1, qadd(a[i], pii(dp[i], i)), smax(ans, pii(dp[i], i));
	ansl = ans.fi;
	for (int x = ans.se; x; x = pre[x]) v[x] = 1;
}

inline void work() {
	rebuild(n + 1);
	for (int i = n; i; --i) {
		ans[i] = ansl;
		if (v[tp[i]]) rebuild(i);
	}
	for (int i = 1; i <= n; ++i) printf("%d%c", ans[i], " \n"[i == n]);
}

inline void init() {
	read(n);
	for (int i = 1; i <= n; ++i) read(a[i]);
	for (int i = 1; i <= n; ++i) read(tp[i]), t[tp[i]] = i;
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	int T;
	read(T);
	while (T--) {
		init();
		work();
	}
	fclose(stdin), fclose(stdout);
	return 0;
}

hdu6619 多校2019Day4F Horse

题目传送门

https://vjudge.net/problem/HDU-6619

http://acm.hdu.edu.cn/showproblem.php?pid=6619

题解

一直以为第二个操作是能量增加吃树高度,没想到是达到吃树高度。


\(s_i\) 表示 \(a_i\) 的前缀和,\(ss_i\) 表示 \(s_i\) 的前缀和。

如果没有这两种操作的话,那么总贡献就是 \(\sum\limits_{i=1}^n s_i = \sum\limits_{i=1}^n (n-i+1)a_i\)

那么如果有了第一种操作的话,那么如果选择 \(i\) 这个点相当于就是把答案贡献了 \((n-i+1)a_i\)。所以可以选择 \(m\) 个最大的 \((n-i+1)a_i\)

但是第二个操作的直接设置可能会破坏掉这个东西。不妨把第二个操作看成直接设成 \(0\),然后保留之前的所有吃树的贡献。

这样,第二个操作就和第一个操作无关了。这个就相当于把原序列分成 \(k+1\) 段,每一段的贡献是 \(\sum\limits_{i=1}^n (s_i - s_{l-1})\)

所以可以直接 dp,很容易发现这个 dp 式子可以用斜率优化转移。


时间复杂度 \(O(nk)\)

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 10000 + 7;
const int M = 50 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;

int n, k, m;
ll ans;
int a[N], s[N], q[N];
ll b[N], ss[N], dp[M][N];

inline void work1() {
	for (int i = 1; i <= n; ++i) b[i] = (ll)a[i] * (n - i + 1);
	std::sort(b + 1, b + n + 1, std::greater<ll>());
	for (int i = 1; i <= m; ++i) ans += b[i];
}

inline ll slope_y(const int &i, const int &j, const int &k) { return (dp[i - 1][k] - ss[k] + (ll)s[k] * k) - (dp[i - 1][j] - ss[j] + (ll)s[j] * j); }
inline ll slope_x(const int &j, const int &k) { return s[k] - s[j]; }

inline void work2() {
	for (int i = 1; i <= n; ++i) ss[i] = ss[i - 1] + s[i];
	for (int i = 1; i <= n; ++i) dp[0][i] = INF;
	for (int j = 1; j <= k; ++j) {
		int hd = 0, tl = 0;
		for (int i = 1; i <= n; ++i) {
			while (hd < tl && slope_y(j, q[tl], i) * slope_x(q[tl - 1], q[tl]) <= slope_y(j, q[tl - 1], q[tl]) * slope_x(q[tl], i)) --tl;
			q[++tl] = i;
			while (hd < tl && slope_y(j, q[hd], q[hd + 1]) <= i * slope_x(q[hd], q[hd + 1])) ++hd;
			dp[j][i] = dp[j - 1][q[hd]] + ss[i] - ss[q[hd]] - (ll)s[q[hd]] * (i - q[hd]);
		}
	}
	ans -= dp[k][n];
}

inline void work() {
	work1();
	work2();
	printf("%lld\n", ans);
}

inline void cls() {
	memset(dp, 0, sizeof(dp));
	ans = 0;
}

inline void init() {
	cls();
	read(n), read(k), read(m), ++k;
	for (int i = 1; i <= n; ++i) read(a[i]), s[i] = s[i - 1] + a[i];
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	int T;
	read(T);
	while (T--) {
		init();
		work();
	}
	fclose(stdin), fclose(stdout);
	return 0;
}

hdu6637 多校2019Day6D peed Dog

题目传送门

https://vjudge.net/problem/HDU-6637

题解

首先最优方案一定是在两个人花费的时间一样的情况下最优对吧。

那么如果每个人都选择自己的时间较少的来做。但是这样就不一定能保证时间一样了。

为了协调时间的同事保证最大时间最小,所以我们优先选择自己的任务所花时间比上另一个人的较大的移动。

也就是说,如果我们把每一项任务按照 \(\frac {a_i}{b_i}\) 排序,那么一定是前边一段是第一个人做,后面一段是第二个人做,中间存在一个合作的。花费时间相等出现在合作的那一项。

想要找到这一段可以二分,找到以后推一波式子就可以求出答案了。

但是现在要按照顺序来加入。所以可以用树状数组维护前后缀的和。


时间复杂度 \(O(n\log^2 n)\)

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 2500000 + 7;

#define lowbit(x) ((x) & -(x))

int n;
int a[N], b[N], p[N], h[N];

struct BIT {
	int s[N];
	inline void qadd(int x, int k) {
		for (; x <= n; x += lowbit(x))
			s[x] += k;
	}
	inline int qsum(int x) {
		int ans = 0;
		for (; x; x -= lowbit(x)) ans += s[x];
		return ans;
	}
	inline int qsum(int l, int r) { return qsum(r) - qsum(l - 1); }
} pr, sf;

inline bool check(int x) { return pr.qsum(1, x - 1) <= sf.qsum(x, n); }

inline bool cmp(const int &x, const int &y) { return (ll)a[x] * b[y] < (ll)a[y] * b[x]; }
inline void work() {
	for (int i = 1; i <= n; ++i) p[i] = i;
	std::sort(p + 1, p + n + 1, cmp);
	for (int i = 1; i <= n; ++i) h[p[i]] = i;//, dbg("p[%d] = %d\n", i, p[i]);
	for (int i = 1; i <= n; ++i) {
		pr.qadd(h[i], a[i]);
		sf.qadd(h[i], b[i]);
		int l = 1, r = n;
		while (l < r) {
			int mid = (l + r + 1) >> 1;
			if (check(mid)) l = mid;
			else r = mid - 1;
		}
//		dbg("l = %d\n", l);
		int pre = pr.qsum(1, l - 1), suf = sf.qsum(l + 1, n);
		assert(p[l] <= i);
		ll ans1 = (ll)pre * b[p[l]] + (ll)suf * a[p[l]] + (ll)a[p[l]] * b[p[l]], ans2 = a[p[l]] + b[p[l]], p = std::__gcd(ans1, ans2);
		assert(p);
		printf("%lld/%lld\n", ans1 / p, ans2 / p);
	}
}

inline void init() {
	read(n);
	memset(pr.s, 0, sizeof(int) * (n + 1));
	memset(sf.s, 0, sizeof(int) * (n + 1));
	for (int i = 1; i <= n; ++i) read(a[i]), read(b[i]);
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	int T;
	read(T);
	while (T--) {
		init();
		work();
	}
	fclose(stdin), fclose(stdout);
	return 0;
}

hdu6587 多校2019Day1J Kingdom

题目传送门

https://vjudge.net/problem/HDU-6587

题解

如果不存在这样的 \(0\) 的点,那么我们回忆一下应该怎样构造一棵树。

首先对于在中序遍历中找到先序遍历第一个点,可以通过这个位置把中序遍历分成两部分,从而推出先序遍历的两部分,递归解决即可。

所以考虑令 \(dp[i][l][r]\) 表示以先序遍历第 \(i\) 个点为根,中序遍历中 \(l .. r\) 的这一段是一个子树的方案数。

那么如果 \(a_i \neq 0\)\(a_i\)\(b\) 出现过的话,那么可以直接以这个位置把 dp 分解成两个子问题这样 dp 下去;

如果 \(a_i \neq 0\)\(a_i\) 未在 \(b\) 中出现过,那么 \(b_{l.. r}\) 里面每一个 \(0\) 都有可能是 \(a_i\),所以枚举这些点来分解即可。

如果 \(a_i = 0\),那么 \(b_{l..r}\) 中每一个 \(0\) 或者是没有在 \(a\) 中出现过的 \(b_i\) 都有可能是 \(a_i\) 对应的东西。所以还是枚举这些点分解。

但是我们可以发现,对于 \(0\) 对应 \(0\) 的情况,我们并没有指出这些点应该是哪个编号。我们找出所有的既没有在 \(a\) 中出现过,也没有在 \(b\) 中出现过的数,设其个数为 \(k\),那么他们可以任意支配这些编号,所以方案还需要再乘上 \(k!\)


时间复杂度 \(O(Tn^4)\)。这竟然能过……我傻了都。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 100 + 7;
const int P = 998244353;

int n;
int a[N], b[N], aa[N], bb[N];
int dp[N][N][N];

inline int smod(int x) { return x >= P ? x - P : x; }
inline void sadd(int &x, const int &y) { x += y; x >= P ? x -= P : x; }
inline int fpow(int x, int y) {
	int ans = 1;
	for (; y; y >>= 1, x = (ll)x * x % P) if (y & 1) ans = (ll)ans * x % P;
	return ans;
}

inline void work() {
	memset(dp, 0, sizeof(dp));
	for (int i = 1; i <= n + 1; ++i)
		for (int j = 1; j <= n + 1; ++j) dp[i][j][j - 1] = 1;
	for (int i = n; i; --i) {
		for (int j = 1; j <= n; ++j)
			for (int k = j; k <= n - i + j && k <= n; ++k) {
				if (a[i]) {
					if (bb[a[i]]) {
						if (bb[a[i]] >= j && bb[a[i]] <= k) dp[i][j][k] = (ll)dp[i + 1][j][bb[a[i]] - 1] * dp[i + bb[a[i]] - j + 1][bb[a[i]] + 1][k] % P;
						else dp[i][j][k] = 0;
					} else for (int l = j; l <= k; ++l) if (!b[l]) sadd(dp[i][j][k], (ll)dp[i + 1][j][l - 1] * dp[i + l - j + 1][l + 1][k] % P);
				} else for (int l = j; l <= k; ++l) if (!b[l] || !aa[b[l]]) sadd(dp[i][j][k], (ll)dp[i + 1][j][l - 1] * dp[i + l - j + 1][l + 1][k] % P);
			}
	}
	int kk = 0, kkk = 1;
	for (int i = 1; i <= n; ++i) if (!aa[i] && !bb[i]) ++kk;
	while (kk) kkk = (ll)kkk * (kk--) % P;
	kkk = (ll)kkk * dp[1][1][n] % P;
	printf("%d\n", kkk);
}

inline void init() {
	read(n);
	memset(aa, 0, sizeof(int) * (n + 1));
	memset(bb, 0, sizeof(int) * (n + 1));
	for (int i = 1; i <= n; ++i) read(a[i]), a[i] && (aa[a[i]] = i);
	for (int i = 1; i <= n; ++i) read(b[i]), b[i] && (bb[b[i]] = i);
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	int T;
	read(T);
	while (T--) {
		init();
		work();
	}
	fclose(stdin), fclose(stdout);
	return 0;
}

hdu6625 多校2019Day5B three arrays

题目传送门

https://vjudge.net/problem/HDU-6625

题解

建立两棵 Trie,然后每一次都用两个点从根向下跑。如果可以走相同的方向(方向就是 \(0/1\))那么就尽量走,否则就走剩下的方向。这样可以保证每一次取到的都是尽量优的解。

但是这样会有一个问题,就是如果有一个时刻,两个方向都可以走的话,我们无法判断应该走哪一个方向。不过我们可以发现两个方向其实都可以走,而且是相互独立的,不会干扰,所以可以随便走一个,剩下的一个留给下一次走。

时间复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 1e5 + 7;

#define lc c[0]
#define rc c[1]

int n, m, nod1 = 1, nod2 = 1;
int ans[N];

struct Node { int c[2], val; } t1[N * 31], t2[N * 31];
inline void ins(Node *t, int &nod, int x) {
	int o = 1;
	++t[o].val;
	for (int i = 30; ~i; --i) {
		int p = (x >> i) & 1;
		if (!t[o].c[p]) t[o].c[p] = ++nod;
		o = t[o].c[p], ++t[o].val;
	}
}

inline int qry() {
	int o1 = 1, o2 = 1, ans = 0;
	--t1[o1].val, --t2[o2].val;
	for (int i = 30; ~i; --i) {
		if (t1[t1[o1].lc].val && t2[t2[o2].lc].val) o1 = t1[o1].lc, o2 = t2[o2].lc, --t1[o1].val, --t2[o2].val;
		else if (t1[t1[o1].rc].val && t2[t2[o2].rc].val) o1 = t1[o1].rc, o2 = t2[o2].rc, --t1[o1].val, --t2[o2].val;
		else if (t1[t1[o1].lc].val && t2[t2[o2].rc].val) o1 = t1[o1].lc, o2 = t2[o2].rc, --t1[o1].val, --t2[o2].val, ans += 1 << i;
		else if (t1[t1[o1].rc].val && t2[t2[o2].lc].val) o1 = t1[o1].rc, o2 = t2[o2].lc, --t1[o1].val, --t2[o2].val, ans += 1 << i;
	}
	return ans;
}

inline void work() {
	for (int i = 1; i <= n; ++i) ans[i] = qry();
	std::sort(ans + 1, ans + n + 1);
	for (int i = 1; i <= n; ++i) printf("%d%c", ans[i], " \n"[i == n]);
	memset(t1, 0, sizeof(Node) * (nod1 + 1));
	memset(t2, 0, sizeof(Node) * (nod2 + 1));
}

inline void init() {
	read(n);
	nod1 = 1, nod2 = 1;
	int x;
	for (int i = 1; i <= n; ++i) read(x), ins(t1, nod1, x);
	for (int i = 1; i <= n; ++i) read(x), ins(t2, nod2, x);
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	int T;
	read(T);
	while (T--) {
		init();
		work();
	}
	fclose(stdin), fclose(stdout);
	return 0;
}
posted @ 2019-11-05 19:18  hankeke303  阅读(441)  评论(0编辑  收藏  举报