10.22集训解题报告

T1 work

100

题面:

大锤最近开了一家公司,有 \(N\) 个人来应聘,第 \(i\) 个人能够连续工作 \(a_i\) 个小时,然
后需要连续休息 $ b_i$ 个小时才能再次工作.
作为一个黑心老板,大锤想让招到的员工能够成为永动机.
具体来说,假设大锤找了 \(k\) 个人 \(w_1,\dots,w_k\) (顺序由你安排),她希望这些人能够
按照一个固定的顺序循环工作.
同时,大锤希望能够招最少的人来满足条件.但是她的数学不好,所以请你助她完成招人的工作.

思路:

如果有方案,如何判断合法呢。
对于每一个人,其他人的工作时间的总和必须大于等于这个人的休息时间,这个人才能休息好,才能续上,继续工作。
形式化的有:

\[b_i\le\sum{a_j}(j≠i) \]

不愿意看到西格玛,所以改成:

\[b_i\le S-a_i \]

其中 \(S\) 是所有人的 \(a\) 的总和。
那么移项:

\[b_i+a_i\le S \]

不难发现,只需满足

\[max\{a_i+b_i\}\le S。\ \]

那么从大到小枚举 \(a+b\) ,以这个为最大值,将剩下的 \(a\) 组成的降序序列从前往后找,就能保证以最小的人数组成大于选的这个 \(a+b\) 的数,以使其合法。
那么这个“剩下的 \(a\) 组成的降序序列”,又要能维护前缀和,还能找第一个大于等于 \(a+b\) 的前缀和的位置,还能删除,复杂度还不能太高,套上个 \(FHQ\) 就行。

代码:

struct tree {
	ll val, sum;
	int l, r, size, ran;
}t[2000006]; 
struct node {
	ll a, b;
}sz[500005];
int sta[500005];
bool cmp(node x, node y) {
	return x.a+x.b>y.a+y.b;
}
int n, head_size, rt;
int New(int val) {
	int g=++head_size;
	t[g].sum=t[g].val=val;
	t[g].ran=rand();
	t[g].size=1;
	return g;
}
void push_up(int g) {
	t[g].size=t[t[g].l].size+t[t[g].r].size+1;
	t[g].sum=t[t[g].l].sum+t[t[g].r].sum+t[g].val;
}
void Split(int g, int &x, int &y, int val) {
	if(g==0) {
		x=y=0;
		return ;
	}
	if(t[g].val<=val) {
		y=g; Split(t[g].l, x, t[y].l, val);
	}
	else {
		x=g; Split(t[g].r, t[x].r, y, val);
	}
	push_up(g);
}
int Merge(int x, int y) {
	if(x==0||y==0) return x+y;
	if(t[x].ran<t[y].ran) {
		t[x].r=Merge(t[x].r, y);
		push_up(x); return x;
	} else {
		t[y].l=Merge(x, t[y].l);
		push_up(y); return y;
	}
}
int Min(ll k) {
	int re=0;
	int g=rt;
	while(g) {
		if(t[t[g].l].sum>=k) g=t[g].l;
		else if(t[t[g].l].sum+t[g].val>=k) {
			return re+t[t[g].l].size+1;
		}
		else {
			k-=t[t[g].l].sum+t[g].val;
			re+=t[t[g].l].size+1;
			g=t[g].r;
		}
	}
	return 1e9;
}
void build() {
	for(int i=n; i>=1; --i) {
		rt=Merge(rt, New(sta[i]));
	}
}
signed main(){
	n=read();
	for(int i=1; i<=n; ++i) {
		sz[i].a=read(); sz[i].b=read(); sta[i]=sz[i].a;
	} sort(sta+1, sta+n+1); sort(sz+1, sz+n+1, cmp);
	build();
	int ans=1e9;
	for(int i=1; i<=n; ++i) {
		ans=min(ans, Min(sz[i].a+sz[i].b));
		int T1, T2, T3;
		Split(rt, T1, T2, sz[i].a);
		Split(T2, T2, T3, sz[i].a-1);
		T2=Merge(t[T2].l, t[T2].r);
		rt=Merge(Merge(T1, T2), T3);
	}
	if(ans==1e9) ans=-1;
	cout<<ans;
}

T2 seq

60

题面:

大锤是一个序列问题砖家,尤其擅长算公共子序列.这一天,她,改变了世界,发明了波浪公共子序列.
定义一个序列是波浪序列,当且仅当这个序列满足没有连续三个元素,满足

对于一个序列 \(a = \{a_i\},\forall{i}\in(1,k),a_i-1 \lt a_i \gt a_i+1 或者 a_i−1 \gt a_i \lt a_i=i.\)

给出两个序列 \(A,B\),求 \(A,B\) 的最长公共波浪子序列.

思路:

\(n^4暴力DP\)

代码:

int dp[5003][5003][2];
int g[5003][2];
int a[5003], b[5003];
int ans;
int main(){
	int n=read();
	for(int i=1; i<=n; ++i) a[i]=read();
	int m=read();
	for(int j=1; j<=m; ++j) b[j]=read();
	for(int i=1; i<=n; ++i) {
		for(int j=1; j<=m; ++j) {
			if(a[i]!=b[j]) continue ;
			dp[i][j][0]=dp[i][j][1]=1;
			for(int k1=1; k1<i; ++k1) {
				if(a[k1]==a[i]) continue ;
				for(int k2=1; k2<j; ++k2) {
					if(a[k1]!=b[k2]) continue ;
					if(a[k1]>a[i]) 
						dp[i][j][0]=max(dp[i][j][0], dp[k1][k2][1]+1);
					if(a[k1]<a[i])  
						dp[i][j][1]=max(dp[i][j][1], dp[k1][k2][0]+1);
				}
			}
			ans=max(max(ans, dp[i][j][0]), dp[i][j][1]);
		}
	}
	cout<<ans;
}

T3 jump

题面:

大锤有一个闺蜜,她们约定每月见一次面.但是她们都非常忙,需要非常精
确的安排才能见到面.
大锤所在的国家可以看成是数轴上的 \(N\) 个城市,依次标号成 $1,\dots, N $ 在每个城市\(i\)有一个传送器,可以将一个人传送 \([\max(1,i-a_i),\min(n,i+a_i)]\) 中的任何一个
城市,且需要花费 \(1\) 块钱.

由于她们常年奔波各地,大锤和她的闺蜜将会随机出现在这个 \(N\) 个位置中的

一个。每次见面前,两人会提前告知对方自己的位置,然后其中一个人会保持不动,另一个人用尽可能少的花费与其汇合.

现在大锤想知道,最坏情况下两个人需要花费多少钱才能够相遇。

思路:

\(n\)\(Dij\)

代码:

int n;
struct edge {
	int t, n;
}e[1000006];
int head[1003], head_size;
void ADD(int f, int t) {
	e[++head_size]=(edge) {t, head[f]};
	head[f]=head_size;
}
int a[100005];
int dis[1003][1003];
struct node {
	int v, diss;
	bool operator < (const node &a) const {
		return a.diss<diss;
	}
};
void Dij(int S) {
	dis[S][S]=0;
	priority_queue<node>q;
	q.push((node){S, 0});
	while(q.size()) {
		node op=q.top(); q.pop();
		if(op.diss!=dis[S][op.v]) continue ;
		for(int i=head[op.v]; i; i=e[i].n) {
			if(dis[S][op.v]+1<dis[S][e[i].t]) {
				dis[S][e[i].t]=dis[S][op.v]+1;
				q.push((node){e[i].t, dis[S][e[i].t]});
			}
		}
	}
}
int main(){
	n=read();
	if(n==1) {
		cout<<0;
		return 0;
	}
	memset(dis, 0x3f, sizeof(dis));
	for(int i=1; i<=n; ++i) {
		a[i]=read();
		for(int j=max(1, i-a[i]); j<=min(n, i+a[i]); ++j) {
			if(i!=j) ADD(i, j);
		}
	}
	for(int i=1; i<=n; ++i) {
		Dij(i);
	}
	int ans=0;
	for(int i=1; i<=n; ++i) {
		for(int j=1; j<i; ++j) {
			ans=max(ans, min(dis[i][j], dis[j][i]));
		}
	}
	cout<<ans;
}
posted @ 2022-10-22 22:50  Konnya_ku  阅读(58)  评论(0编辑  收藏  举报