Loading

2021牛客暑期多校训练营1 部分题目

题目 (6/11) A B C D E F G H I J K
通过情况 o o o
  • 赛中通过 √
  • 赛后通过 o

A - Alice and Bob

题意:
有两堆石子,他们的数量分别是\(n,m\),然后\(Alice,Bob\)进行游戏,每轮游戏可以进行下列操作,先从任意一堆的石子拿出\(k\ (k>0)\)个石子,再从另一堆石子中拿出\(s * k,(s \ge 0)\)个石子,谁先不能进行操作谁就输了。
然后\(Alice\)先手,给定\(T\)组数据,每一组\(n,m,(0\le n,m\le 5000)\)问谁会赢的游戏。

思路:
赛中一直都在找必败态的规律,毫无收获。
这道题从数据范围入手,\(5000\)的大小再加上\(T\)组数据,有点预处理的味道,赛时队友提了一下,当时应该考虑到的。
我们直接小到大,暴力地用小数据的状态去更新大数据的转态,也就是按照题意的描述对当前的两堆石子进行一步操作的模拟,假如能够到达某一个必败态/必胜态,相应地也就表明了我们当前的位置是必胜态/必败态。
所以直接暴力枚举出来所有\(Bob\)会赢的情况,因为实测这个情况会少一些,然后直接把答案存下来,\(O(1)\)查询就可以了。

博弈类型的题目还是做得少了,欠缺一些基本的思路和经验。

#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 5e3 + 10;
std::set<pii>s = {...省略打表结果}
int f[N][N];

int solve(int x,int y) {
	for(int i = 1;i <= x;i ++) {
		for(int j = 0;j * i <= y;j ++) {
			if(!f[x - i][y - j * i]) return 1;
		}
	}
	for(int i = 1;i <= y;i ++) {
		for(int j = 0;j * i <= x;j ++) {
			if(!f[x - i * j][y - i]) return 1;
		}
	}
	return 0;
}

int main() {

	// int n = 5000;
	// for(int i = 0;i <= n;i ++) {
	// 	for(int j = i;j <= n;j ++) {
	// 		f[i][j] = f[j][i] = solve(i,j);
	// 		if(!f[i][j]) {
	// 			printf("{%d,%d},",i,j);
	// 		}
	// 	}
	// }
	// cout << sz(s) << '\n';
	int T;scanf("%d",&T);
	while(T--) {
		int n,m; scanf("%d%d",&n,&m);
		if(n > m) swap(n,m);
		if(s.count({n,m})) printf("Bob\n");
		else printf("Alice\n");
	}
	return 0;
}

J - Journey among Railway Stations

题意:
现在有\(n\)个车站,对于每一个车站\(i\)来说,它都有一个固定的开放时间也就是\([u_i,v_i]\),如果火车的到达时间早于\(u_i\),那么他可以等待然后穿过这个站\(i\),但是如果到达时间晚于\(v_i\)的话,那么就不能继续前进了,然后站\(i\)\(i+1\)有一个时间花费\(cost_i\),在每一站内停车和通过的时间忽略不计。

现在有\(Q\)次操作,每次操作分别属于下面的三中类型之一:
\(1.\ 0 \ l \ r\),代表询问从区间[\(l,r\)]能否合法通过。
\(2.\ 1 \ i \ w\),表示将\(cost_i\)改为\(w\)
\(3.\ 2 \ i \ p \ q\),表示讲第\(i\)个区间的开放时间段改为\([p,q]\)

思路:
看到区间操作,我们可以想能否数数据结构维护什么东西来解决。
首先我们把题目中的限制条件有数学式子来表示一下,此处以区间\([1,3]\)为例。
首先\(1\)如果能够到达\(2\),那么条件就是\(u_1 + cost_1 <= v_2\),如果\(2\)能够到达\(3\)那么条件就是\(max(u_1 + cost_1 + cost_2,u_2 + cost_2) <= v_3\)
此时我们发现式子两侧是不好对应的,此时我们如果
\(u^{'}_i = u_i + cost_i + cost_{i+1} + ... + cost_{n-1}\)
\(v^{'}_i = v_i + cost_i + cost_{i+1} + ... + cost_{n-1}\)
此时我们需要判断的限制条件就变为了
\(max(u^{'}_1)\) \(<=\) \(v^{'}_2\),\(max(\) \(u^{'}_1\), \(u^{'}_2\)) \(<=\) \(v^{'}\)
然后我们令线段树的每个节点维护的的当前代表的区间是否合法并且当前区间内的\(u^{'}_{max}\)\(v^{'}_{min}\)
每次合并两个左右子节点的时候需要判断左右子节点是否均合法,且左儿子的\(max\)是否小于等于右儿子的\(min\),满足这样的条件才可以父节点才能标记为合法。
更新操作分别就是区间更新和单点更新了,比较常规,需要注意的一点就是,因为既涉及到了区间更新也涉及到了单点更新,所以另写单点更新的函数时,别忘了\(pushdown\ lazy\)

ps:多思考有没有对题目条件的变式把他变成线段树可维护的东西

#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e6 + 10;
int n,q;
ll u[N],v[N],cost[N],sumcost[N],lazy[N << 2];

struct segT {
	ll umax,vmin;
	bool flag;
}tree[N << 2];

void pushup(int rt) {//两端更新的条件
	tree[rt].umax = max(tree[lson].umax,tree[rson].umax);
	tree[rt].vmin = min(tree[lson].vmin,tree[rson].vmin);
	if(tree[lson].flag && tree[rson].flag && tree[lson].umax <= tree[rson].vmin)
		tree[rt].flag = true;
	else tree[rt].flag = false;
}

void pushdown(int rt) {
	if(lazy[rt]) {
		lazy[lson] += lazy[rt];
		lazy[rson] += lazy[rt];
		tree[lson].umax += lazy[rt];
		tree[lson].vmin += lazy[rt];
		tree[rson].umax += lazy[rt];
		tree[rson].vmin += lazy[rt];
		lazy[rt] = 0;
	}
}

void build(int rt,int l,int r) {
	lazy[rt] = 0;
	if(l == r) {
		tree[rt].umax = u[l] + sumcost[l];
		tree[rt].vmin = v[l] + sumcost[l];
		tree[rt].flag = true;
		return ;
	}
	int mid = (l + r) >> 1;
	build(lson,l,mid); build(rson,mid+1,r);
	pushup(rt);
}

void potmodify(int rt,int l,int r,int p,ll v,char ch) {
	if(l == r) {
		if(ch == 'u') {
			tree[rt].umax += v; return ;
		}
		else {
			tree[rt].vmin += v; return ;
		}
	}
	int mid = (l + r) >> 1;
    pushdown(rt);
	if(p <= mid) potmodify(lson,l,mid,p,v,ch);
	else potmodify(rson,mid+1,r,p,v,ch);
	pushup(rt); 
}

void segmodify(int rt,int l,int r,int L,int R,ll v) {
	if(l >= L && r <= R) {
		tree[rt].umax += v;
		tree[rt].vmin += v;
		lazy[rt] += v;
		return ;
	}
	int mid = (l + r) >> 1;
	pushdown(rt);
	if(L <= mid) segmodify(lson,l,mid,L,R,v);
	if(R > mid) segmodify(rson,mid+1,r,L,R,v);
	pushup(rt);
}

segT query(int rt,int l,int r,int L,int R) {
	if(l >= L && r <= R) {
		return tree[rt];
	}
	int mid = (l + r) >> 1;
	pushdown(rt);
	if(L > mid) return query(rson,mid+1,r,L,R);
	else if(R <= mid) return query(lson,l,mid,L,R);
	else {
		segT t1 = query(lson,l,mid,L,R),t2 = query(rson,mid+1,r,L,R),t;
		t.umax = max(t1.umax,t2.umax);
		t.vmin = min(t1.vmin,t2.vmin);
		if(t1.flag && t2.flag && t1.umax <= t2.vmin) t.flag = true;
		else t.flag = false;
		return t;
	}
}

void solve() {
	scanf("%d",&n);
	for(int i = 1;i <= n;i ++) sumcost[i] = 0;
	for(int i = 1;i <= n;i ++) scanf("%lld",&u[i]);
	for(int i = 1;i <= n;i ++) scanf("%lld",&v[i]);
	for(int i = 1;i <= n - 1;i ++) scanf("%lld",&cost[i]);
	for(int i = n - 1;i >= 1;i --) sumcost[i] = sumcost[i + 1] + cost[i];//后缀和
	build(1,1,n);
	scanf("%d",&q);
	int op,i;
	ll l,r,w,x,y;
	while(q--) {
		scanf("%d",&op);
		if(op == 0) {
			scanf("%d%d",&l,&r);
			segT res = query(1,1,n,l,r);
			if(res.flag) puts("Yes");
			else puts("No");
		}
		else if(op == 1) {
			scanf("%d%lld",&i,&w);
			ll v = w - cost[i];
			segmodify(1,1,n,1,i,v);
			cost[i] = w;
		}
		else if(op == 2) {
			scanf("%d%lld%lld",&i,&x,&y);
			// x -= u[i];
			// y -= v[i];
			potmodify(1,1,n,i,x - u[i],'u');
			potmodify(1,1,n,i,y - v[i],'v');
			u[i] = x,v[i] = y;
		}
	}
}

int main() {
	int T;scanf("%d",&T);
	while(T--) {
		solve();
	}
	return 0;
}
posted @ 2021-07-18 17:54  x7x7g7c7  阅读(191)  评论(0)    收藏  举报