Live2D

2020-11-30 考试总结

本来以为不会挂分了,但还是挂了45。。。本来不挂前面几名就稳了。

T1 ZZH的游戏

题目传送门

Description

ZZH 在和 GVZ 玩游戏。

ZZH 和 GVZ 各有一棵树,每棵树都有 \(n\) 个点。

两棵树上各自有一枚棋子。ZZH 的棋子初始在它树上的点 \(s\) ,GVZ 的棋子初始在树上的点 \(t\)

两人轮流操作。一个人操作时,可以选择不移动自己树上的棋子,也可以选择将自己树上的棋子移动到自己树上相邻的一个点。

当两枚棋子都在所在树的点 \(1\) 上时,游戏结束。游戏的分数是所有时刻中,两个棋子所在的点编号的和的最大值。

ZZH 和 GVZ 会合作让游戏结束时的分数尽量小。两人都极其聪明,因此两人都会采用最优策略。

ZZH 和 GVZ 一共会进行 \(T\) 次游戏,每次游戏的树与之前不同, \(n\) 也不一定相同,但两人的树的点数一定相等。

你需要求出在每一次游戏中,游戏结束时的分数的最小值。

\(\sum{n}\leq 10^6,T\leq 10^4\)

Solution

不难想到,可以二分答案,然后我们贪心得想的话,肯定时u先走到能走到的编号最小的点,然后v走到能走到的编号最小的点,然后u走到能走到的编号最小的点,v走到能走到的编号最小的点,如此循环往复,判断最后两个点是否都可以走到1即可。

考虑如何优化,不难看出我们可以对于每一条边 \((a,b)\)\(\max(a,b)\) 排序,每次可以走的话就把 \(a,b\) 用并查集连起来。然后维护编号最小点即可。时间复杂度 \(\Theta(n\log^2 n)\)

但是这样并不能过,考虑去掉二分,可以看出,每次当我们走不动的时候我们把答案+1即可。时间复杂度 \(\Theta(n\log n)\)

\(\texttt{Code}\)

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

#define Int register int
#define MAXN 1000005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int tim,n,S,T;

struct Edge{
	int u,v,w;
	Edge(){}
	Edge (int _u,int _v,int _w){u = _u,v = _v,w = _w;}
	bool operator < (const Edge &p)const{return w < p.w;}
}e1[MAXN],e2[MAXN];

int lim,st[2],mi[2],miget1[MAXN],miget2[MAXN];

int fa1[MAXN],fa2[MAXN];
int findSet1 (int x){return fa1[x] == x ? x : fa1[x] = findSet1 (fa1[x]);}
int findSet2 (int x){return fa2[x] == x ? x : fa2[x] = findSet2 (fa2[x]);}
void unionSet1 (int u,int v){
	int mi1 = miget1[findSet1 (u)],mi2 = miget1[findSet1 (v)];
	fa1[findSet1 (u)] = findSet1 (v),
	miget1[fa1[v]] = min (mi1,mi2);
}
void unionSet2 (int u,int v){
	int mi1 = miget2[findSet2 (u)],mi2 = miget2[findSet2 (v)];
	fa2[findSet2 (u)] = findSet2 (v),
	miget2[fa2[v]] = min (mi1,mi2);
}

void modify (int now,int get){
	if ((get == 0 && findSet1 (now) == findSet1 (S)))
		mi[get] = min (mi[get],miget1[findSet1 (now)]);
	else if (get == 1 && findSet2 (now) == findSet2 (T))
		mi[get] = min (mi[get],miget2[findSet2 (now)]);
}

void Work (){
	lim = S + T,st[0] = st[1] = 1,mi[0] = S,mi[1] = T;
	for (Int i = 1;i <= n;++ i) fa1[i] = fa2[i] = miget1[i] = miget2[i] = i;
	int las[2] = {1,1};
	for (Int now = 0;now <= 4 * n;++ now){
		int get = now & 1;
		while (st[get] <= n - 1 && (get == 0 ? e1[st[get]].w : e2[st[get]].w) <= lim - mi[get ^ 1]){
			if (get == 0) unionSet1 (e1[st[get]].u,e1[st[get]].v),modify (e1[st[get]].u,0),modify (e1[st[get]].v,0);
			else unionSet2 (e2[st[get]].u,e2[st[get]].v),modify (e2[st[get]].u,1),modify (e2[st[get]].v,1);
			st[get] ++;
		}
		if (findSet1 (1) == findSet1 (S) && findSet2 (1) == findSet2 (T)){
			write (lim),putchar ('\n');
			return ;
		}
		if (now & 1){
			if (st[0] == las[0] && st[1] == las[1]) lim ++;
			else las[0] = st[0],las[1] = st[1];
		}
	}
	write (lim),putchar ('\n');
}

signed main(){
//	freopen ("game.in","r",stdin);
//	freopen ("game.out","w",stdout);
	read (tim);
	while (tim --> 0){
		read (n);
		for (Int i = 2,u,v;i <= n;++ i) read (u),read (v),e1[i - 1] = Edge (u,v,max (u,v));
		for (Int i = 2,u,v;i <= n;++ i) read (u),read (v),e2[i - 1] = Edge (u,v,max (u,v));
		read (S),read (T),sort (e1 + 1,e1 + n),sort (e2 + 1,e2 + n);
		Work ();
	}
	return 0;
}

T2 ZZH与背包

题目传送门

Description

ZZH 有一个背包。

ZZH有 n 个物品,第 i 个物品的体积为 \(v_i\)

ZZH 要去学校,她想带 n 个物品中的一些去学校。为了使背包不过于空,放入背包中的物品体积总和不能小于 l 。因为背包有容量上限,所以放入背包中的物品体积总和不能大于 r。

ZZH 想知道,她能带去学校的物品的集合一共有多少种。ZZH觉得这个问题太简单了,于是她把这个问题交给了你。

ZZH一共要去 q 次学校,因为ZZH还有一些必须带的东西(例如显卡),所以每次的 l,r 会改变。你需要对于每一个给出的 l,r 求出答案。

\(1\leq n \leq 40, q \leq 500, v_i \leq 10^9, 1 \leq l_i \leq r_i \leq \sum{v_i}\)

Solution

不难看出我们可以折半搜索,然后你每次直接从前往后扫一遍就好了。

时间复杂度 \(\Theta(q2^{n/2})\)

\(\texttt{Code}\)

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

#define Int register int
#define int long long
#define MAXN 45

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int n,q,v[MAXN];

vector <int> get1,get2;

int my_lowerbound (vector <int> &S,int v){
	int l = 0,r = S.size() - 1,ans = r + 1;
	while (l <= r){
		int mid = (l + r) >> 1;
		if (S[mid] >= v) ans = mid,r = mid - 1;
		else l = mid + 1;
	}
	return ans;
}

signed main(){
	read (n),read (q);
	int all = 1ll << n;
	for (Int i = 1;i <= n;++ i) read (v[i]); 
	int len = (n <= 32 ? n / 3 : n / 2);
	for (Int S = 0;S < (1ll << len);++ S){
		int now = 0;
		for (Int i = 1;i <= len;++ i) if (S >> i - 1 & 1) now += v[i];
		get1.push_back (now);
	}
	sort (get1.begin(),get1.end());
	len = n - len;
	for (Int S = 0;S < (1ll << len);++ S){
		int now = 0;
		for (Int i = 1;i <= len;++ i) if (S >> i - 1 & 1) now += v[(n <= 32 ? n / 3 : n / 2) + i];
		get2.push_back (now);
	}
	sort (get2.begin(),get2.end());
	while (q --> 0){
		int l,r,ans = 0;read (l),read (r);int ed = my_lowerbound (get2,l - get1[0]);
		for (Int i = 0;i < get1.size() && get1[i] < l;++ i){
			int now = get1[i];
			while (ed && get2[ed - 1] >= l - now) -- ed;
			ans += ed;
		}
		int L = 0,R = get1.size() - 1,res = 0;
		while (L <= R){
			int mid = (L + R) >> 1;
			if (my_lowerbound (get2,r - get1[mid] + 1) == get2.size()) res = mid,L = mid + 1;
			else R = mid - 1;
		}
		ed = get2.size();ed = my_lowerbound (get2,r - get1[0] + 1);
		for (Int i = res;i < get1.size();++ i){
			int now = get1[i];
			while (ed && get2[ed - 1] >= r - now + 1) -- ed;
			ans += get2.size() - ed;
		}
		write (all - ans),putchar ('\n');
	}
	return 0;
}

T3 ZZH与计数

题目传送门

Description

ZZH 喜欢计数。

ZZH 有很多的数,经过统计,ZZH一共有 \(v_0\) 个 0 ,\(v_1\) 个 1,...,\(v_{2^{n}-1}\)\(2^{n} - 1\) 。因为一些原因,ZZH 只有这 \(2^{n}\) 种数。

ZZH 和 GVZ 要对这些数进行 m 次操作。每一次操作由一个人进行。每一次,有 p 的概率由 ZZH 操作, 1 - p 的概率由 GVZ 操作。

两人进行操作的时候都会依次操作每一个数。对于一个数 s ,如果 ZZH 对这个数进行操作,她会在 \(0,1,...,2^{n} - 1\) 中找出所有的 t,满足 t or s = s,然后将 s 等概率随机变成找出的 t 中的一个。

如果 GVZ 对这个数进行操作,她会在 \(0,1,...,2^{n} - 1\) 中找出所有的 t,满足 t and s = s ,然后将等概率随机变成找出的 t 中的一个。(这里的and/or指二进制与/或操作)

因为操作需要非常长的时间,她们想要知道所有操作结束后,对于每一个 i ,i 的个数的期望。因为期望值可能不是整数,所以她们想知道期望值模 的结果。

因为她们觉得这个问题太简单了,于是她们把这个问题交给了你。

Solution

还不会,先咕着。

话说本来以为这个题区分度会很大,不过似乎都是暴力。(为什么没人写30啊???

T4 ZZH的旅行

题目传送门

Description

给一棵有根树,\(1\) 为根。对于每个点 \(x\) ,求出对于满足如下条件的序列 \({s_1,...,s_k}\)

  1. \(s_{i-1}\)\(s_i\) 的祖先,且 $s_{i-1} != s_i $

  2. \(s_1 = x\)

中,$ \sum_{i=2}^k(a_{s_{i-1}} - dis(s_{i-1},s_i))b_{s_i} $ 的最大值。

\(n \leq 10^6, 0 \leq a_i,b_i,d_i \leq 10^9\)

Solution

不难看出,设 \(f_u\) 为从 \(u\) 的答案,那么存在转移式:

\[f_u=\max_{v\in tree_u}\{f_v+(a_u+dep_u-dep_v)\times b_v\} \]

然后你发现这个东西我们可以当成一个斜率为 \(b_v\),截距为 \(f_v-dep_v\times b_v\) 的直线在 \(x=a_u+dep_u\) 的取值。然后说白了你就是要维护一堆直线,然后问在 \(x\) 的取值的最高点。

于是问题就变成了李超线段树合并板子。

时间复杂度 \(\Theta(n\log n)\),空间动态开点回收空间之后可以做到 \(\Theta(n)\)

\(\texttt{Code}\)

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

#define get yourmothersget
#define Int register int
#define int long long
#define MAXN 1000005

char buf[1<<17],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<17,stdin),p1==p2)?EOF:*p1++)
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int un,tmp[MAXN];
int n,tot,siz,K[MAXN],B[MAXN];
signed son[MAXN * 31][2],tree[MAXN * 31];

inline int f (int x,int l){return tmp[l] * K[x] + B[x];}
inline int newnode (){return ++ siz;}

inline void modify (signed &id,int l,int r,int x){
	if (!id) id = newnode ();
	if (l == r){
		if (f (x,l) > f(tree[id],l)) tree[id] = x;
		return ;
	}
	int mid = (l + r) >> 1;
	if (K[tree[id]] < K[x]){
		if (f (tree[id],mid) < f (x,mid)) modify (son[id][0],l,mid,tree[id]),tree[id] = x;
		else modify (son[id][1],mid + 1,r,x);
	}
	else{
		if (f (tree[id],mid) > f (x,mid)) modify (son[id][0],l,mid,x);
		else modify (son[id][1],mid + 1,r,tree[id]),tree[id] = x;
	}
}

inline int query (int id,int l,int r,int x){
	if (!id) return 0;
	if (l == r) return f (tree[id],x);
	int mid = (l + r) >> 1;
	if (x <= mid) return max (query (son[id][0],l,mid,x),f (tree[id],x));
	else return max (query (son[id][1],mid + 1,r,x),f (tree[id],x));
}

inline void clear (int u){
	son[u][0] = son[u][1] = tree[u] = 0;
}

inline void Merge (int l,int r,signed &u,int v){
	if (!u || !v) return u = u + v,void ();
	if (l == r){
		if (f (tree[u],l) < f (tree[v],l)) tree[u] = tree[v];
		if (v) clear (v);
		return ;
	}
	int mid = (l + r) >> 1;
	if (tree[v]) modify (u,l,r,tree[v]);
	Merge (l,mid,son[u][0],son[v][0]);
	Merge (mid + 1,r,son[u][1],son[v][1]);
	if (v) clear(v);
}

struct Edge{
	int v,w;
};
vector <Edge> G[MAXN];

#define son bitch
int a[MAXN],b[MAXN],dep[MAXN],get[MAXN],size[MAXN];
signed rt[MAXN],son[MAXN];

inline void dfs (int u,int fa){
	size[u] = 1;
	for (Edge to : G[u]) if (to.v ^ fa){
		int v = to.v,w = to.w;
		dep[v] = dep[u] + w,dfs (v,u),size[u] += size[v];
		if (size[v] > size[son[u]]) son[u] = v;
	}
}

inline void maintain (int u,int fa){
	if (son[u]) maintain (son[u],u),rt[u] = rt[son[u]];
	for (Edge to : G[u]) if (to.v != fa && to.v != son[u]) maintain (to.v,u),Merge (1,un,rt[u],rt[to.v]);
	int id = lower_bound (tmp + 1,tmp + un + 1,a[u] + dep[u]) - tmp;
	get[u] = query (rt[u],1,un,id),K[++ tot] = b[u],B[tot] = get[u] - dep[u] * b[u],modify (rt[u],1,un,tot);
}

signed main(){
	read (n);
	for (Int i = 1;i <= n;++ i) read (a[i]),read (b[i]);
	for (Int i = 2,u,v,w;i <= n;++ i) read (u),read (v),read (w),G[u].push_back (Edge {v,w}),G[v].push_back (Edge {u,w});
	dfs  (1,0);for (Int i = 1;i <= n;++ i) tmp[i] = a[i] + dep[i];sort (tmp + 1,tmp + n + 1),un = unique (tmp + 1,tmp + n + 1) - tmp - 1;
	maintain (1,0);for (Int i = 1;i <= n;++ i) write (get[i]),putchar ('\n'); 
	return 0;
}
posted @ 2020-11-30 20:27  Dark_Romance  阅读(191)  评论(0编辑  收藏  举报