HGOI 20200725

又是自闭的一天

T1 首都(capital)

换根dp

首先以1为根搜出所有的反边

然后dp

\(dp\)\(i\) 时,\(i\) 的父亲已经知道答案

只需考虑 \((fa,i)\) 这条边

正为 \(+1\),反为 \(-1\)

#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second

const int N = 200010 ;

int n, ans ;
int val[N], res[N] ;
vector <pair<int, int> > e[N] ;

void dfs1(int u, int ft) {
	rep(i, 0, siz(e[u]) - 1) {
		int v = e[u][i].fi ;
		if (v == ft) continue ;
		dfs1(v, u) ;
		val[u] += val[v] + e[u][i].se ;
	}
}

void dfs2(int u, int ft, int v1, int v2) {
	res[u] = val[1] - v1 + v2 ;
	ans = min(ans, res[u]) ;
	rep(i, 0, siz(e[u]) - 1) {
		int v = e[u][i].fi ;
		if (v == ft) continue ;
		if (e[u][i].se) dfs2(v, u, v1 + 1, v2) ;
		else dfs2(v, u, v1, v2 + 1) ;
	}
}

signed main() {
	scanf("%d", &n) ;
	rep(i, 1, n - 1) {
		int u, v ; scanf("%d%d", &u, &v) ;
		e[u].pb(mp(v, 0)) ;
		e[v].pb(mp(u, 1)) ;
	}
	ans = 2 * n ;
	dfs1(1, 0) ;
	dfs2(1, 0, 0, 0) ;
	printf("%d\n", ans) ;
	rep(i, 1, n) if (res[i] == ans) printf("%d ", i) ;

	return 0 ;
}

T2 maxmin

这个题有点意思

题意是查所有子区间 \([i,j](i<j)\) 中最大值-最小值 在 \([l,r]\) 范围内的个数

\(n*q<=2*10^6\) 提示我们是 \(O(nq)\)

首先预处理 \((l,r)\) 的区间中最小值和最大值

\([l,r]\) 的个数 = 在 \([0,r]\) 的个数-在 \([0,l-1]\) 的个数

然后考虑 \(two-pointers\)

对于一个 \(l\) 我们求出他最大的满足的 \(r\)

算两次

就可以了

其实可以用单调队列,有点类似滑动窗口

可以AC

#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second

const int N = 500010 ;

int n, q ;
int mx[N][25], mi[N][25], a[N], ans1[N], ans2[N] ;

void build() {
	rep(i, 1, n) mx[i][0] = mi[i][0] = a[i] ;
	rep(j, 1, 20) 
	rep(i, 1, n - (1 <<j) + 1) {
		mx[i][j] = max(mx[i][j - 1], mx[i + (1 << (j - 1))][j - 1]) ;
		mi[i][j] = min(mi[i][j - 1], mi[i + (1 << (j - 1))][j - 1]) ;
    }
}

int query(int x, int y) {
	if (x > y) swap(x, y) ;
	int k = (double) log(y - x + 1) / log(2.0) ; 
	return max(mx[x][k], mx[y - (1 << k) + 1][k]) - min(mi[x][k], mi[y - (1 << k) + 1][k]) ;
}

ll solve(int L, int R) {
    ll ans = 0 ;
    for (int i = 1, r = 1; i <= n; i++) {
    	while (r + 1 <= n && query(i, r + 1) < L) r++ ;
    	ans1[i] = r ;
	}
	for (int i = 1, r = 1; i <= n; i++) {
		while (r + 1 <= n && query(i, r + 1) <= R) r++ ;
		ans2[i] = r ;
	}
	rep(i, 1, n) if (ans1[i] <= ans2[i]) ans += ans2[i] - ans1[i] ;
	return ans ;
}

signed main() {
	scanf("%d%d", &n, &q) ;
	rep(i, 1, n) scanf("%d", &a[i]) ;
	build() ;
	while (q--) {
		int l, r ; scanf("%d%d", &l, &r) ;
		printf("%lld\n", solve(l, r)) ;
	}
	return 0 ;
}

T3 简单数论题(number)

出题人你有毒吧

直接放板子题,然后 \(n\)\(10^18\)

洛谷模板的第一个问题

代码就不放了

猜你喜欢

转载自www.cnblogs.com/harryhqg/p/13376726.html