ARC073简要题解

在这里插入图片描述

VP赛中只过了两题的菜狗竟是我自己
A题:
简单记录一下上次开水闸的时间,判一判就好。

#include <bits/stdc++.h>
using namespace std;
int n, T;
int a[200010];
int sth[500010], scnt;
int main() { 
	scanf("%d%d", &n, &T);
	for(int i=1;i<=n;++i) 
		scanf("%d", &a[i]);
	int las = 0;
	sth[++scnt] = las;
	a[++n] = 2100000000;
	for(int i=2;i<=n;++i) { 
		if(las + T < a[i]) 
			sth[++scnt] = las + T, sth[++scnt] = a[i], las = a[i];
		else 
			las = a[i];
	} 
	int ans = 0;
	for(int i=2;i<=scnt;i+=2) 
		ans += sth[i] - sth[i-1];
//	for(int i=1;i<=scnt;++i) 
//		printf("%d : %d\n",i,sth[i]);
	cout<<ans;
	return 0;
} 

B题:
发觉他一共才100个元素,而且重量一共就四种诶。
那么对于每个重量,枚举一下这个重量有几个,判一判能不能塞进去。
拿的时候同一个重量贪心就好。

#include <bits/stdc++.h>
using namespace std;
int n;
int sth[4][100];
int pos[4];
int w1;
int W;
long long ans = -123123123;
bool gwei(int a, int b, int c, int d) { 
	long long Twei = 1ll * w1 * a + 1ll * (w1 + 1) * b + 1ll * (w1 + 2) * c + 1ll * (w1 + 3) * d;
	if(Twei <= W) { 
		long long nans = sth[0][a] + sth[1][b] + sth[2][c] + sth[3][d];
		ans = max(ans, nans);
		return true;
	} 
	return false;
} 
int main() { 
	scanf("%d%d", &n, &W);
	int nw, nv;
	scanf("%d%d", &nw, &nv);
	w1 = nw, sth[0][++pos[0]] = -nv;
	for(int i=2;i<=n;++i) { 
		scanf("%d%d", &nw, &nv);
		int det = nw - w1;
		sth[det][++pos[det]] = -nv;
	} 
	for(int i=0;i<=3;++i) 
		sort(sth[i] + 1, sth[i] + pos[i] + 1);
	for(int i=0;i<=3;++i) { 
		for(int j=1;j<=pos[i];++j) { 
			sth[i][j] = -sth[i][j];
			sth[i][j] += sth[i][j-1];
		} 
	} 
	for(int i=0;i<=pos[0];++i) { 
		for(int j=0;j<=pos[1];++j) { 
			for(int k=0;k<=pos[2];++k) { 
				for(int l=0;l<=pos[3];++l) { 
					if(!gwei(i, j, k, l)) 
						break;
				} 
			} 
		}
	} 
	cout<<ans;
	return 0;
} 

C题:
开局读了个假题,以为是让我求最大。
“这不抽出头尾几个元素写个爆搜瞎判一发就好了?”
写完了发现样例过不去,手玩也不对。
重新读了个题,原来是最小值。
“这不把抽头尾改成抽中间,然后max改min就好了?”
然后发觉自己假的有点猛,隧弃疗,赛后膜了下题解。
定义 M A X MAX MAX M I N MIN MIN分别为全局max和全局min,分情况讨论。
假设 M A X MAX MAX M I N MIN MIN分别在红色和蓝色,那么显然,对于每两个球,把大的那个丢给 M A X MAX MAX,小的那个丢给 M I N MIN MIN,一定最优。
如果 M A X MAX MAX M I N MIN MIN在同一个颜色,也就是说,这个颜色的 M − m M-m Mm已经是定值了。
那么,我们把所有的二元组先内部排序,然后再外部排序,这样之后,我们依次做红蓝swap这个操作。在第一次做之后,我们会进入" M I N 和 M A X MIN和MAX MINMAX在同一个颜色里面"的状态,之后按顺序swap,可以保证一定对于另一个pack拿到最小值。
来证明一下上面那个“保证”
分配好 M A X MAX MAX M I N MIN MIN之后,我们先无脑把所有的较小值扔进未确定的包里面。
然后按照较小值从小到大排个序,依次swap两个包里面的值。
对于MAX和MIN所在的那个包裹,无论如何加值,都不会造成影响。
对于另一个包裹,我们保证了他的Min是单调取遍每个值的。且我们做了最少的事情使得这个Min拿到这个值,也就是说,我们对于max的更改是满足贪心的。
然后用multiset做就好,注意.erase的时候,里面要套个.find,不然会挂。

#include <bits/stdc++.h>
using namespace std;
pair<int, int>sth[200010];
multiset<int>R, B;
int n;

long long gans() { 
	return 1ll * (*R.rbegin() - *R.begin()) * (*B.rbegin() - *B.begin());
} 

int main() { 
	scanf("%d", &n);
	for(int i=1;i<=n;++i) { 
		int ma, mi;
		scanf("%d%d",&ma, &mi);
		if(ma < mi) 
			swap(ma, mi);
		sth[i] = make_pair(mi, ma);
		R.insert(mi), B.insert(ma);
	} 
	sort(sth+1, sth+n+1);
	long long ans = 0x3f3f3f3f3f3f3f3f;
	ans = min(ans, gans());
	for(int i=1;i<=n;++i) { 
		R.erase(R.find(sth[i].first)), B.erase(B.find(sth[i].second));
		R.insert(sth[i].second), B.insert(sth[i].first);
		ans = min(ans, gans());
	} 
	cout<<ans;
	return 0;
} 

F题:
首先可以想到一个很显然的 O ( n 2 ) O(n^2) O(n2)的DP算法:
dp[i][j]表示,当前完成了前i个指令,有一个棋子在x[i],另一个在j的最小花费。
考虑转移,在输入第 i + 1 i+1 i+1个指令的时候,有两种决策:把位于x[i]的棋子挪过去 / 把位于j的棋子挪过去。
对于第一种决策,可以写作 d p [ i + 1 ] [ j ] = d p [ i ] [ j ] + d i s t ( x [ i ] , x [ i + 1 ] ) dp[i+1][j] = dp[i][j] + dist(x[i], x[i+1]) dp[i+1][j]=dp[i][j]+dist(x[i],x[i+1])
对于第二种决策,可以写作 d p [ i + 1 ] [ x [ i ] ] = m i n ( d p [ i ] [ j ] + d i s t ( j , x [ i + 1 ] ) ) dp[i+1][x[i]] = min(dp[i][j] + dist(j, x[i+1])) dp[i+1][x[i]]=min(dp[i][j]+dist(j,x[i+1]))
两种决策是取min关系。
发觉第一种我们可以通过维护一个全局Delta来实现,因为全局加的是一个固定值。
第二种决策我们需要查询带绝对值的min(因为有dist),不好操作,拆成两段。
上线段树,线段树中的val[1]和val[2]分别是 D P [ ? ] [ l o c ] − D e l t a − l o c DP[?][loc]-Delta-loc DP[?][loc]Deltaloc D P [ ? ] [ l o c ] − D e l t a + l o c DP[?][loc]-Delta+loc DP[?][loc]Delta+loc,改的时候注意推一推怎么加怎么减。
最后全局查个min就好。
写的时候注意线段树里面的那个mid要记得除二啊!!!这东西我调了30min。

#include <bits/stdc++.h>
using namespace std;
int n, q, A, B;
int x[200010];

namespace sgt {
#define ls(k) a[k].ch[0]
#define rs(k) a[k].ch[1]
	struct node { 
		int ch[2], l, r;
		long long val[3];
	}; node a[500010];
	int cnt = 1;
	inline void update(int k) { 
		for(int i=1;i<=2;++i) 
			a[k].val[i] = min(a[ls(k)].val[i], a[rs(k)].val[i]);
	} 
	inline void build(int k, int l, int r) { 
		a[k].l = l, a[k].r = r;
		if(l == r) { 
			a[k].val[1] = 1e15;
			a[k].val[2] = 1e15;
			return;
		} 
		int mid = l + r >> 1;
		ls(k) = ++cnt, build(ls(k), l, mid);
		rs(k) = ++cnt, build(rs(k), mid+1, r);
		update(k);
	} 
	inline void cmin(int k, int tar, long long val) { 
		if(a[k].l == a[k].r) { 
			a[k].val[1] = min(a[k].val[1], val - a[k].l);
			a[k].val[2] = min(a[k].val[2], val + a[k].l);
			return;
		} 
		int mid = a[k].l + a[k].r >> 1;
		if(tar <= mid)
			cmin(ls(k), tar, val);
		else
			cmin(rs(k), tar, val);
		update(k);
	}
	inline void change(int k, int tar, long long val) { 
		if(a[k].l == a[k].r) { 
			a[k].val[1] = val - a[k].l;
			a[k].val[2] = val + a[k].l;
			return;
		} 
		int mid = a[k].l + a[k].r >> 1;
		if(tar <= mid)
			change(ls(k), tar, val);
		else
			change(rs(k), tar, val);
		update(k);
	}
	long long query(int k,int l,int r,int val) { 
		if(a[k].l == l && a[k].r == r) 
			return a[k].val[val];
		int mid = a[k].l + a[k].r >> 1;
		if(r <= mid) 
			return query(ls(k), l, r, val);
		else if(l > mid) 
			return query(rs(k), l, r, val);
		else 
			return min(query(ls(k), l, mid, val), query(rs(k), mid+1, r, val));
	} 
	long long gans(int k) { 
		if(a[k].l == a[k].r) 
			return a[k].val[1] + a[k].l;
		return min(gans(ls(k)), gans(rs(k)));
	} 	
} using namespace sgt;
long long suplus = 0;

int ab(int a) { 
	return a > 0 ? a : -a;
} 

long long Q(int l,int r,int type) { 
	if(l > r || l < 0 || r > n) 
		return 40000000000000000;
	return query(1, l, r, type);
} 

int main() { 
	scanf("%d%d", &n, &q);
	scanf("%d%d", &A, &B);
	for(int i=1;i<=q;++i) { 
		scanf("%d",&x[i]);
	} 
	x[0] = A;
	build(1, 1, n);
	change(1, B, 0);
//	printf("Cmin : %d %lld\n", B, 0);
//	printf("Q 1 1 1 = %lld\n",Q(1,1,1));
	for(int i=1;i<=q;++i) {
//		printf("Q 1 1 1 = %lld\n",Q(1,1,1));
		//树里面放的是,dp[i] - suplus的值。
		int det = ab(x[i-1] - x[i]);
		long long lmin = Q(1, x[i], 1);
		long long rmin = Q(x[i], n, 2);
		long long actdp = min(x[i] + lmin, rmin - x[i]) + suplus;
//		printf("lmin = %lld rmin = %lld actdp = %lld suplus = %lld  det = %lld\n", lmin, rmin, actdp, suplus, det);
		suplus += det;
//		printf("Cmin : %d %lld\n", x[i-1], actdp - suplus);
		cmin(1, x[i-1], actdp - suplus);
//		printf("Q 1 1 1 = %lld\n",Q(1,1,1));
	} 
	cout<<gans(1) + suplus;
	return 0;
} 

一场ARC下来感觉自己菜的不行,果然太久没打个人赛是会GG

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值