杭电多校部分题解

HDU7215 Weighted Beautiful Tree

  • u p x up_x upx 表示 x x x 连向其父结点的那条边的权值, f x , 0 / 1 f_{x,0/1} fx,0/1 表示令 w n x wn_x wnx 小于等于/大于等于 u p x up_x upx 时,将以 x x x 为根的子树中所有结点的 w n wn wn 全部修改为合法的最小代价。
  • x x x 的所有子结点 y y y 按照 u p y up_y upy 从小到大排序, u p y up_y upy u p x up_x upx w n x wn_x wnx 的取值划分成若干区间,取值在区间内部的决策是唯一的,不难得到转移。
  • 另外当 w n x wn_x wnx u p y up_y upy 时,可能会有多个 y y y u p y up_y upy 相同,此时它们的 f y , 0 , f y , 1 f_{y,0},f_{y,1} fy,0,fy,1 均是有意义的,需要特殊处理。
  • 两种情况均可以用前缀和优化,时间复杂度 O ( n log ⁡ n ) \mathcal O(n \log n) O(nlogn)
#include <bits/st dc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 1e5 + 5;
const int N2 = 2e5 + 5;
const ll Maxn = 1e18;
const int mod = 998244353;
int T_data, n, cm, mx = 1e6;
int wn[N], fa[N], c[N], cur[N], up[N];
ll f[N][2], pre[N], suf[N], sum[N];

struct arc
{
	int to, cst;
	arc *nxt;
}p[N2], *adj[N], *T = p;

inline void linkArc(int x, int y, int w)
{
	(++T)->nxt = adj[x]; adj[x] = T; T->to = y; T->cst = w;
	(++T)->nxt = adj[y]; adj[y] = T; T->to = x; T->cst = w; 
}

inline int calc(int x, int l, int r)
{
	if (x >= l && x <= r)
		return 0;
	return x < l ? l - x : x - r;	
}

inline bool cmp(const int &x, const int &y)
{
	return up[x] < up[y];
}

inline void dfs(int x)
{
	for (arc *e = adj[x]; e; e = e->nxt)
	{
		int y = e->to;
		if (y == fa[x])
			continue ;
		fa[y] = x;
		up[y] = e->cst;
		dfs(y);			
	}
	cm = 0;
	for (arc *e = adj[x]; e; e = e->nxt)
	{
		int y = e->to;
		if (y == fa[x])
			continue ;
		cur[++cm] = y;
	}
	if (!cm)
	{
		f[x][0] = 1ll * c[x] * calc(wn[x], 0, up[x]);
		f[x][1] = 1ll * c[x] * calc(wn[x], up[x], mx + 1);
	}
	else
	{
		std::sort(cur + 1, cur + cm + 1, cmp);
		up[cur[0] = 0] = 0;
		up[cur[cm + 1] = n + 1] = mx + 1;
	
		pre[0] = 0;
		for (int i = 1; i <= cm; ++i)
			pre[i] = pre[i - 1] + f[cur[i]][0];
		suf[cm + 1] = 0;
		for (int i = cm; i >= 1; --i)
			suf[i] = suf[i + 1] + f[cur[i]][1];
		sum[0] = 0;
		for (int i = 1; i <= cm; ++i)
			sum[i] = sum[i - 1] + Min(f[cur[i]][0], f[cur[i]][1]);
		for (int i = 1; i <= cm + 1; ++i)
		{
			if (up[cur[i - 1]] <= up[x])
				CkMin(f[x][0], pre[i - 1] + suf[i] 
					+ 1ll * c[x] * calc(wn[x], up[cur[i - 1]], Min(up[cur[i]], up[x])));
			if (up[cur[i]] >= up[x])
				CkMin(f[x][1], pre[i - 1] + suf[i]
					+ 1ll * c[x] * calc(wn[x], Max(up[cur[i - 1]], up[x]), up[cur[i]]));	
		} 

		for (int l = 1, r = 1; l <= cm; l = r + 1)
		{
			r = l;
			while (r < cm && up[cur[r + 1]] == up[cur[r]])
				++r;
			if (up[cur[r]] <= up[x])
				CkMin(f[x][0], pre[l - 1] + suf[r + 1] + sum[r] - sum[l - 1]
					+ 1ll * c[x] * Abs(wn[x] - up[cur[r]]));
			if (up[cur[r]] >= up[x])
				CkMin(f[x][1], pre[l - 1] + suf[r + 1] + sum[r] - sum[l - 1]
				   	+ 1ll * c[x] * Abs(wn[x] - up[cur[r]]));
		}
	}
}

int main()
{
	read(T_data);
	for (int t = 1; t <= T_data; ++t)
	{
		read(n);
		for (int i = 1; i <= n; ++i)
			f[i][0] = f[i][1] = Maxn;
		for (int i = 1; i <= n; ++i)
			read(c[i]);
		for (int i = 1; i <= n; ++i)
			read(wn[i]);
		for (int i = 1, x, y, z; i < n; ++i)
		{
			read(x); read(y); read(z);
			linkArc(x, y, z);
		}
		fa[1] = 0;
		up[1] = wn[1];
		dfs(1);
		put(Min(f[1][0], f[1][1])), putchar('\n');
		
		up[n + 1] = up[0] = 0;
		for (int i = 1; i <= n; ++i)
			adj[i] = NULL, up[i] = fa[i] = 0;
		T = p;
	}
	return 0;
}

HDU7224 Ironforge

  • [ L i , R i ] [L_i,R_i] [Li,Ri] 表示从点 i i i 出发最远能走到的区间范围,预处理 L i , R i L_i,R_i Li,Ri 则可 O ( 1 ) \mathcal O(1) O(1) 回答询问。
  • 先倒序循环 i i i,计算出从点 i i i 出发一直向右走最远能走到哪里,先存在 R i R_i Ri 内,计算的过程相当于合并连续段,因此总移动次数 O ( n ) \mathcal O(n) O(n)
  • 正序循环 i i i,计算 L i , R i L_i,R_i Li,Ri,具体分情况讨论:
    • R i − 1 ≥ i R_{i - 1} \ge i Ri1i 时,若从 [ i , R i ] [i,R_i] [i,Ri] 可以往回走到 i − 1 i - 1 i1,则直接令 L i = L i − 1 , R i = R i − 1 L_i = L_{i - 1},R_{i} = R_{i - 1} Li=Li1,Ri=Ri1,否则直接令 L i = i L_i = i Li=i R i R_i Ri 为预处理的值。
    • R i − 1 < i R_{i - 1} < i Ri1<i 时,此时有 R i > R i − 1 R_i > R_{i - 1} Ri>Ri1,暴力移动求 L i , R i L_i,R_i Li,Ri,计算 L i L_i Li 的过程也相当于合并连续段,且 R i R_i Ri 尝试移动的次数和 L i L_i Li 相同,因此两者的总移动次数均为 O ( n ) \mathcal O(n) O(n)
  • 判断区间内是否存在某个质因子可在 vector 上二分,时间复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
typedef vector<int>::iterator it;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 2e5 + 5;
const int Maxn = 1e9;
const int Minn = -1e9;
const int mod = 998244353;
int T_data, n, m, pn, tis, lim = 2e5;
int a[N], b[N], l[N], r[N];
int vis[N], low[N], pri[N];
vector<int> e[N]; 

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

inline void sieve()
{
	for (int i = 2; i <= lim; ++i)
	{
		if (!low[i])
			pri[++pn] = low[i] = i;
		for (int j = 1; j <= pn && 1ll * i * pri[j] <= lim; ++j)
		{
			low[i * pri[j]] = pri[j];
			if (low[i] == pri[j])
				break ;
		}
	}
}

inline void insert(int x, int p)
{
	if (vis[x] != tis)
	{
		vis[x] = tis;
		e[x].clear();
	}
	e[x].push_back(p); 	
}

inline bool check(int x, int l, int r)
{
 	it p = std::lower_bound(e[x].begin(), e[x].end(), l);
	if (p == e[x].end())
		return false;
	return e[x][p - e[x].begin()] <= r;	
} 

int main()
{
	sieve();
	read(T_data);
	while (T_data--)
	{
		++tis;
		read(n); read(m);
		for (int i = 1; i <= n; ++i)
			read(a[i]);
		for (int i = 1; i < n; ++i)
			read(b[i]);
		for (int i = 1; i <= n; ++i)
		{
			int x = a[i];
			while (low[x] > 1)
			{
				insert(low[x], i);
				x /= low[x];
			}
		}
		for (int i = n; i >= 1; --i)
		{
			r[i] = i;
			while (r[i] < n && check(b[r[i]], i, r[i]))
				r[i] = r[r[i] + 1];
		}
		for (int i = 1; i <= n; ++i)
			if (i > 1 && r[i - 1] >= i)
			{
				if (check(b[i - 1], i, r[i]))
				{
					l[i] = l[i - 1];
					r[i] = r[i - 1];
				}
				else
					l[i] = i;
			}
			else
			{
				l[i] = i;
				bool flag = true;
				while (flag)
				{
					flag = false;
					while (l[i] > 1 && check(b[l[i] - 1], l[i], r[i]))
					{
						flag = true;
						l[i] = l[l[i] - 1];
					}
					while (r[i] < n && check(b[r[i]], l[i], r[i]))
					{
						flag = true;
						r[i] = r[r[i] + 1];
					} 
				}
			}
		int x, y;
		while (m--)
		{
			read(x); read(y);
			if (l[x] <= y && y <= r[x])
				puts("Yes");
			else
				puts("No");
		}
	}
	return 0;
}

HDU7226 Darnassus

  • 取边 ( i , i + 1 ) (i,i+1) (i,i+1) 构成一棵生成树,则得到的生成树内每条边的边权都小于等于 n − 1 n - 1 n1
  • Kruskal \text{Kruskal} Kruskal 算法,最小生成树每条边的边权也小于等于 n − 1 n - 1 n1
  • 因此 ∣ i − j ∣ |i - j| ij ∣ p i − p j ∣ |p_i - p_j| pipj 至少有一项小于等于 n − 1 \sqrt{n - 1} n1 ,总边数 O ( n n ) \mathcal O(n\sqrt n) O(nn )
  • 桶排后用 Kruskal \text{Kruskal} Kruskal 算法求最小生成树即可。
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
typedef pair<int, int> pir; 
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 1e5 + 5;
const int Maxn = 1e9;
const int Minn = -1e9;
const int mod = 998244353;
int T_data, n, S;
int fa[N], q[N], p[N];
vector<pir> v[N];
ll ans;

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

inline bool cmp(const int &x, const int &y)
{
	return p[x] < p[y];
}

inline int ufs_find(int x)
{
	if (fa[x] != x)	
		return fa[x] = ufs_find(fa[x]);
	return x;
}

int main()
{
	read(T_data);
	while (T_data--)
	{
		read(n);
		S = sqrt(n - 1);
		for (int i = 1; i <= n; ++i)
			read(p[i]);
		if (n == 1)
		{
			puts("0");
			continue ;
		}
		for (int i = 1; i <= n; ++i)
			q[i] = i;
		std::sort(q + 1, q + n + 1, cmp);
		for (int i = 1; i < n; ++i)
			for (int j = i + 1, jm = Min(i + S, n); j <= jm; ++j)
			{
				ll z = (j - i) * Abs(p[j] - p[i]);
				if (z <= n - 1)
					v[z].push_back(std::make_pair(i, j));
				z = (j - i) * Abs(q[j] - q[i]);
				if (z <= n - 1)
					v[z].push_back(std::make_pair(q[i], q[j])); 
			}
		for (int i = 1; i <= n; ++i)
			fa[i] = i;
		ans = 0;
		for (int i = 1; i < n; ++i)
		{
			for (pir e : v[i])
			{
				int tx = ufs_find(e.first),
					ty = ufs_find(e.second);
				if (fa[tx] != ty)
				{
					fa[tx] = ty;
					ans += i;
				}
			}
			v[i].clear();
		}
		put(ans), putchar('\n');
	}
	return 0;
}

HDU7233 Arithmetic Subsequence

  • 同种数字出现超过两次显然无解,对于其他情况,按二进制下最低一位为 0/1 分组,显然组与组之间不会产生影响,组内就继续按最低一位不同的 0/1 分组,不断递归下去。
  • 发现最终的顺序实际上就是将 a i a_i ai 按二进制翻转后的数排序,可以简化实现。
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 1e5 + 5;
const int Maxn = 1e9;
const int Minn = -1e9;
const int mod = 998244353;
int T_data, n;
int pos[N], a[N], b[N];
map<int, int> cnt;

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

inline bool cmp(const int &x, const int &y)
{
	return b[x] < b[y];
}

int main()
{
	read(T_data);
	while (T_data--)
	{
		read(n);
		cnt.clear();
		bool flag = false;
		for (int i = 1; i <= n; ++i)
		{
			read(a[i]);
			if (++cnt[a[i]] > 2)
				flag = true;
		}
		if (flag)
			puts("NO");
		else
		{
			puts("YES");
			for (int i = 1; i <= n; ++i)
			{
				b[i] = 0; 
				for (int j = 0; j < 30; ++j)
					if (a[i] >> j & 1)
						b[i] |= (1 << (29 - j));
			}
			for (int i = 1; i <= n; ++i)
				pos[i] = i;
			std::sort(pos + 1, pos + n + 1, cmp);
			for (int i = 1; i <= n; ++i)
				put(a[pos[i]]), putchar(' ');
			puts("");
		}
	}
	return 0;
}

HDU7238 Mario Party

  • 暴力分块,对于每一块,记 l i m = ∑ a i 在块内且 a i < 0 ∣ a i ∣ lim = \sum \limits_{a_i 在块内且 a_i < 0}|a_i| lim=ai在块内且ai<0ai,预处理块内 [ 0 , l i m ] [0, lim] [0,lim] 的答案,询问时不完整块直接暴力,完整块直接用预处理的结果,大于 l i m lim lim 时的迭代过程显然和 l i m lim lim 相同。
  • 时间复杂度 O ( q n + n ∑ i = 1 n ∣ a i ∣ ) \mathcal O(q\sqrt n + \sqrt n \sum\limits_{i = 1}^{n}|a_i|) O(qn +n i=1nai)
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 5e5 + 5;
const int L = 1e3 + 5;
const int M = 1e6 + 5;
const int Maxn = 1e9;
const int Minn = -1e9;
const int mod = 998244353;
int T_data, n, q, S, p[M];
int len[L], a[N], bel[N], bl[L], br[L]; 
int *ans[L], *now = p;

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

inline int Query(int l, int r, int x)
{
	int tl = bel[l], tr = bel[r];
	if (tl == tr)
	{
		int res = x;
		for (int i = l; i <= r; ++i)
			if (res + a[i] >= 0)
				res += a[i];
		return res;
	}
	l == bl[tl] ? --tl : 0;
	r == br[tr] ? ++tr : 0;
	int res = x;
	for (int i = l; i <= br[tl]; ++i)
		if (res + a[i] >= 0)
			res += a[i];
	for (int i = tl + 1; i < tr; ++i)
		if (res <= len[i])
			res = ans[i][res];
		else
			res = ans[i][len[i]] + res - len[i];
	for (int i = bl[tr]; i <= r; ++i)
		if (res + a[i] >= 0)
			res += a[i];
	return res;
}

int main()
{
	read(T_data);
	while (T_data--)
	{
		read(n); read(q);
		S = sqrt(n);
		bl[0] = br[0] = 0;
		for (int i = 1; i <= n; ++i)
		{
			bel[i] = (i - 1) / S + 1;
			if (!bl[bel[i]])
				bl[bel[i]] = i;
			br[bel[i]] = i;
		}
		bl[bel[n] + 1] = br[bel[n] + 1] = n + 1;
		for (int i = 1; i <= n; ++i)
			read(a[i]);
		now = p;
		for (int i = 1; i <= bel[n]; ++i)
		{
			len[i] = 0;
			for (int j = bl[i]; j <= br[i]; ++j)
				if (a[j] < 0) len[i] -= a[j];
			ans[i] = now;
			now += len[i] + 1;
			for (int j = 0; j <= len[i]; ++j)
			{
				ans[i][j] = j;
				for (int k = bl[i]; k <= br[i]; ++k)
					if (ans[i][j] + a[k] >= 0)
						ans[i][j] += a[k];
			}
		}
		int l, r, x;
		while (q--)
		{
			read(l); read(r); read(x);
			++l;
			put(l > r ? x : Query(l, r, x)), putchar('\n');
		}
		for (int i = 0; i <= bel[n] + 1; ++i)
			bl[i] = br[i] = 0;
	}
	return 0;
}

HDU7239 Matryoshka Doll

  • f i , j f_{i,j} fi,j 表示将前 i i i 个元素划分成 j j j 个子序列的方案数。
  • 转移分为以第 i i i 个元素新建一个子序列和将第 i i i 个元素插入到已有的子序列的末尾,对于第二种转移,设 p r e i pre_i prei 表示最大的 k k k 满足 a i − a k ≥ r a_i - a_k \ge r aiakr,则 [ p r e i + 1 , i − 1 ] [pre_i + 1, i - 1] [prei+1,i1] 内的元素都恰好属于某一个子序列,且 i i i 无法再接在这些子序列后,不难得到转移。
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 5e3 + 5;
const int Maxn = 1e9;
const int Minn = -1e9;
const int mod = 998244353;
int T_data, n, k, r;
int a[N], pre[N];
int f[N][N];

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

int main()
{
	read(T_data);
	while (T_data--)
	{
		read(n); read(k); read(r);
		for (int i = 1; i <= n; ++i)
			read(a[i]);
		for (int i = 1; i <= n; ++i)
		{
			pre[i] = pre[i - 1];
			while (pre[i] + 1 < i && a[i] - a[pre[i] + 1] >= r)
				++pre[i];
		}
		for (int i = 0; i <= n; ++i)
			for (int j = 0; j <= k; ++j)
				f[i][j] = 0;
		f[0][0] = 1;
		for (int i = 1; i <= n; ++i)
			for (int j = 1, jm = Min(i, k); j <= jm; ++j)
			{
				add(f[i][j], f[i - 1][j - 1]);
				if (pre[i])
					f[i][j] = (1ll * Max(j - (i - pre[i] - 1), 0) * f[i - 1][j] + f[i][j]) % mod;
			}
		put(f[n][k]), putchar('\n');
	}
	return 0;
}

HDU7240 Shortest Path in GCD Graph

  • 不难发现答案不超过 2,问题即为 [ 1 , n ] [1, n] [1,n] u , v u,v u,v 均互质的个数。
  • 可转化为 [ 1 , n ] [1,n] [1,n] 内与 u , v u,v u,v 的所有出现过的素因子均互质的个数,素因子最多不超过 12 个,直接暴力枚举子集容斥即可。
#include<bits/stdc++.h>

typedef long long ll;
const int N = 1e7 + 3;
const int M = 1e5 + 5;

int pri[N], low[N];
int n, u, v, tot, q;
int p[M];
bool size[N];
ll sum[N];

inline void sieve(int lim)
{
	int pn = 0;
	for (int i = 2; i <= lim; ++i)
	{
		if (!low[i])
			pri[++pn] = low[i] = i;
		for (int j = 1; j <= pn && 1ll * i * pri[j] <= lim; ++j)
		{
			low[i * pri[j]] = pri[j];
			if (low[i] == pri[j])
				break ;
		}
	}
}

inline int read()
{
	int v = 0, c = 1;
	char ch = getchar();
	while (!isdigit(ch))
	{
		if (ch == '-') c = -1;
		ch = getchar();
	}
	while (isdigit(ch))
	{
		v = v * 10 + ch - 48;
		ch = getchar();
	}
	return v * c;
}

inline void work(int x)
{
	while(x != 1)
	{
		int t = low[x];
		p[++tot] = low[x];
		while (x % t == 0)
			x /= t;
	}
}

int main()
{
	n = read(), q = read();
	sieve(n);
	while(q--)
	{
		ll ans = 0;
		tot = 0;
		u = read(), v = read();
		int gc = std::__gcd(u,v);
		if(gc == 1){
			printf("1 1\n");
			continue;	
		}
		ans += gc == 2;
		work(u); work(v);
		std::sort(p + 1, p + tot + 1); 
		tot = std::unique(p + 1, p + tot + 1) - p - 1;
		const int C = 1 << tot;
		for (int i = 0; i < tot; ++i)
			sum[1 << i] = p[i + 1], size[1 << i] = 1;
		ans += n;
		for (int i = 1; i < C; ++i)
		{
			int t = i & -i;
			if (t != i)
			{
				sum[i] = 1ll * sum[i ^ t] * sum[t];
				size[i] = size[i ^ t] ^ 1;
			}
			size[i] ? ans -= 1ll * n / sum[i] : ans += 1ll * n / sum[i];	
		}
		printf("2 %d\n",ans);
	}
    return 0;
}

HDU7248 Apples

  • 设从第 n n n 个人的家运往第 1 个人的家的苹果数量为 x x x,则其余道路运送的苹果数量均能确定。
  • a i = ∑ j = 1 i ( e j − b j ) a_i = \sum \limits_{j = 1}^{i}(e_j - b_j) ai=j=1i(ejbj),则总代价为 ∑ i = 1 n l i ∣ x − a i ∣ \sum \limits_{i = 1}^{n}l_i |x - a_i| i=1nlixai
  • 显然代价最小时 x x x 取的是 a i a_i ai 的加权中位数,将 a i a_i ai 离散化用线段树维护即可。
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::complex;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
typedef complex<ld> com;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 5e5 + 5;
const int N4 = 2e6 + 5;
const int Maxn = 1e9;
const int Minn = -1e9;
const int mod = 998244353;
int T_data, n, q, cm;
int b[N], e[N], l[N], a[N], c[N];
ll num[N], cnt[N4], sum[N4];
ll ans, cnt0, cnt1, sum0, sum1, tot; 

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

#define sL s << 1
#define sR s << 1 | 1

inline void Update(int s)
{
	cnt[s] = cnt[sL] + cnt[sR];
	sum[s] = sum[sL] + sum[sR];
}

inline void Build(int s, int l, int r)
{
	if (l == r)
	{
		cnt[s] = num[l];
		sum[s] = num[l] * c[l];	
		return ;
	}
	int mid = l + r >> 1;
	Build(sL, l, mid);
	Build(sR, mid + 1, r);
	Update(s);
}

inline void Modify(int s, int l, int r, int x)
{
	if (l == r)
	{
		cnt[s] = num[l];
		sum[s] = num[l] * c[l];
		return ;
	}
	int mid = l + r >> 1;
	x <= mid ? Modify(sL, l, mid, x) : Modify(sR, mid + 1, r, x);
	Update(s);
}

inline void Query(int s, int l, int r, ll k)
{	
	if (l == r)	
	{
		ans = c[l];
		return ;
	}
	int mid = l + r >> 1;
	if (k <= cnt[sL])
	{
		cnt1 += cnt[sR];
		sum1 += sum[sR];
	 	Query(sL, l, mid, k);
	}
	else 
	{
		cnt0 += cnt[sL];
		sum0 += sum[sL];
		Query(sR, mid + 1, r, k - cnt[sL]);
	}
}

inline void Clear(int s, int l, int r)
{
	cnt[s] = sum[s] = 0;
	if (l == r)
		return ;
	int mid = l + r >> 1;
	Build(sL, l, mid);
	Build(sR, mid + 1, r); 
}

inline void calc()
{
	cnt0 = cnt1 = sum0 = sum1 = 0;
	Query(1, 1, cm, tot + 1 >> 1);
	put(cnt0 * ans - sum0
	  + sum1 - cnt1 * ans), putchar('\n');
}

int main()
{
	read(T_data);
	while (T_data--)
	{
		read(n);
		cm = tot = 0;
		for (int i = 1; i <= n; ++i)
		{
			read(b[i]); 
			read(e[i]);
			read(l[i]);
			a[i] = e[i] - b[i] + a[i - 1];
			c[++cm] = a[i];
		}
		std::sort(c + 1, c + cm + 1);
		cm = std::unique(c + 1, c + cm + 1) - c - 1;
		for (int i = 1; i <= n; ++i)
		{
			a[i] = std::lower_bound(c + 1, c + cm + 1, a[i]) - c;
			num[a[i]] += l[i];
			tot += l[i];
		}
		Build(1, 1, cm);
		
		calc();
		read(q);
		while (q--)
		{
			int x, y;
			read(x); read(y);
			tot -= l[x];
			num[a[x]] -= l[x];
			l[x] = y;
			tot += l[x];
			num[a[x]] += l[x];
			Modify(1, 1, cm, a[x]);
			calc();	
		}
		Clear(1, 1, cm);
		for (int i = 1; i <= cm; ++i)
			num[i] = 0;
	}
	return 0;
}

HDU7255 Expected Inversions

  • 若已经指定了树根,由期望的线性性,我们枚举点对 ( x , y ) , x > y (x,y), x > y (x,y),x>y
    • x x x y y y 的祖先,对答案的贡献为 1 1 1
    • y y y x x x 的祖先,对答案的贡献为 0 0 0
    • 否则对答案的贡献为 1 2 \frac{1}{2} 21(两者在前和在后的概率显然相同)。
  • 考虑未指定树根的情况,由于第三种情况的求解比较复杂,我们先将所有点对的贡献视为 1 2 \frac{1}{2} 21,再将第一种情况的加上 1 2 \frac{1}{2} 21,第二种情况的减去 1 2 \frac{1}{2} 21
  • 在以 1 为根的树上对上述三种情况做简单讨论,不难将前两种情况的调整转化为查询某点和其子结点的子树内比其编号小的结点数以及在 DFS \text{DFS} DFS 序上做区间加,分别用树状数组维护和在区间上打差分标记即可。
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

template <class T>
inline void _put(T x)
{
	if (x < 0)
		x = -x, putchar('-');
	put(x);
}

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}
template <class T>
inline T Sqr(T x) {return x * x;}

using std::map;
using std::set;
using std::pair;
using std::bitset;
using std::string;
using std::vector;
using std::complex;
using std::multiset;
using std::priority_queue;

typedef long long ll;
typedef long double ld;
typedef complex<ld> com;
const ld pi = acos(-1.0);
const ld eps = 1e-8;
const int N = 1e5 + 5;
const int N2 = 2e5 + 5;
const int Maxn = 1e9;
const int Minn = -1e9;
const int mod = 998244353;
const int inv2 = 499122177;
int T_data, n, tis;
int ans[N], fans[N], dfn[N], c[N], idx[N], low[N], sze[N], fa[N];

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

struct arc
{
	int to;
	arc *nxt;
}p[N2], *adj[N], *P = p;

inline void linkArc(int x, int y)
{
	(++P)->nxt = adj[x]; adj[x] = P; P->to = y; 
	(++P)->nxt = adj[y]; adj[y] = P; P->to = x;
} 

inline void Modify(int x)
{
	for (; x <= n; x += x & -x)
		++c[x];
}

inline int Query(int x)
{
	int res = 0;
	for (; x; x ^= x & -x)
		res += c[x];
	return res;
}

inline void dfs(int x)
{
	sze[x] = 1;
	dfn[x] = ++tis;
	idx[tis] = x;
	for (arc *e = adj[x]; e; e = e->nxt)
	{
		int y = e->to;
		if (y == fa[x])
			continue ;
		fa[y] = x;
		dfs(y);
		sze[x] += sze[y];
	}
	low[x] = tis;
}

inline void segModify(int l, int r, int v)
{
	if (l > r)
		return ;
	add(ans[l], v);
	dec(ans[r + 1], v);
}

int main()
{
	read(T_data);
	while (T_data--)
	{
		read(n);
		for (int i = 1, x, y; i < n; ++i)
		{
			read(x); read(y);
			linkArc(x, y);
		}
		dfs(1);
		for (int i = 1; i <= n; ++i)
		{
			int l0 = Query(low[i]) - Query(dfn[i] - 1),
				l1 = i - 1 - l0,
				g0 = sze[i] - 1 - l0,
				g1 = n - i - g0;
			segModify(dfn[i], low[i], 1ll * (l1 - g1 + mod) * inv2 % mod);
			for (arc *e = adj[i]; e; e = e->nxt)
			{
				int y = e->to;
				if (y == fa[i])
					continue ;
				l0 = Query(low[y]) - Query(dfn[y] - 1); 
				g0 = sze[y] - l0;
				if (l0 != g0)
				{
					int tmp = 1ll * (l0 - g0 + mod) * inv2 % mod;
					segModify(1, dfn[y] - 1, tmp);
					segModify(low[y] + 1, n, tmp);
				}
			}
			Modify(dfn[i]);
		}
		int tmp = 1ll * (n - 1) * n / 2 % mod * inv2 % mod;
		for (int i = 1; i <= n; ++i)
		{
			add(ans[i], ans[i - 1]);
			int res = tmp;
			add(res, ans[i]);
			fans[idx[i]] = res;
		}
		for (int i = 1; i <= n; ++i)
			put(fans[i]), putchar('\n');
		tis = 0;
		for (int i = 0; i <= n + 1; ++i)
			ans[i] = fans[i] = 0;
		for (int i = 1; i <= n; ++i)
			c[i] = fa[i] = idx[i] = sze[i] = dfn[i] = low[i] = 0;
		for (int i = 1; i <= n; ++i)
			adj[i] = NULL;
		P = p;
	}
	return 0;
}

HDU7387 Equivalence

  • T 2 T_2 T2 中每条树边的边权一定是 T 1 T_1 T1 中这两点间的距离,因而对于固定的 T 1 T_1 T1,对应的 T 2 T_2 T2 方案唯一,只需考虑 T 2 T_2 T2 中由若干树边组成路径的合法性。
  • 假设 T 2 T_2 T2 中某两条树边在 T 1 T_1 T1 中的路径有交,则 T 2 T_2 T2 中一定存在一条简单路径恰好包含这两条树边,这条简单路径的两个端点在 T 1 T_1 T1 中对应的路径一定只会经过交集一次, 而沿着 T 2 T_2 T2 的树边会经过交集至少两次,因而交集的所有边权必须为 0。
  • 因此只需要对 T 2 T_2 T2 的每条树边,做 T 1 T_1 T1 中对应的路径的覆盖,最后需要修改的边数就是边权不为 0 且至少被覆盖了两次的边数。
#include <bits/stdc++.h>

using std::ios;
using std::cin;
using std::cout;
using std::vector;

const int N = 1e6 + 5;
int anc[N][22], delta[N], dep[N], p2[N], p1[N], val[N];
vector<int> e[N];
int T_data, n, ans;

inline int queryLCA(int x, int y)
{
    if (x == y) 
        return x;
    if (dep[x] < dep[y])
        std::swap(x, y);
    for (int i = 19; i >= 0; --i)
    {
        if (dep[anc[x][i]] >= dep[y])
            x = anc[x][i];
        if (x == y)
            return x;
    }
    for (int i = 19; i >= 0; --i)
        if (anc[x][i] != anc[y][i])
            x = anc[x][i], y = anc[y][i];
    return anc[x][0];
}

inline void dfsInit(int x)
{
    dep[x] = dep[anc[x][0]] + 1;
    for (int i = 0; anc[x][i]; ++i)
        anc[x][i + 1] = anc[anc[x][i]][i];
    for (int y : e[x])
    {
        anc[y][0] = x;
        dfsInit(y);
    }
}

inline void dfsCalc(int x)
{
    for (int y : e[x])
    {
        dfsCalc(y);
        delta[x] += delta[y];
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    
    cin >> T_data;
    while (T_data--)
    {
        cin >> n;
        for (int i = 2; i <= n; ++i)
        {
            cin >> p1[i];
            e[p1[i]].emplace_back(i);
        }
        for (int i = 2; i <= n; ++i)
            cin >> val[i];
        dfsInit(1);
        for (int i = 2; i <= n; ++i)
        {
            cin >> p2[i];
            int z = queryLCA(i, p2[i]);
            ++delta[i], ++delta[p2[i]];
            delta[z] -= 2;
        }
        dfsCalc(1);
        ans = 0;
        for (int i = 2; i <= n; ++i)
            if (val[i] && delta[i] >= 2)
                ++ans;
        cout << ans << '\n';
        for (int i = 1; i <= n; ++i)
        {
            e[i].clear();
            delta[i] = 0;
            dep[i] = 0;
            for (int j = 0; anc[i][j]; ++j)
                anc[i][j] = 0;
        }
    }    
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值