ARC077简要题解

本文记录了一场激烈的算法比赛,作者分享了在C题中利用双端队列解决奇偶性问题,D题中通过组合计数避免重复,以及E题中模拟操作并应用区间加法解决环形路径问题的心得。同时,作者反思了赛中状态不佳及对F题的理解困难。
摘要由CSDN通过智能技术生成

感觉是有史以来打的最痛苦的一场VP
在这里插入图片描述
这罚时就离谱。
C题:
双端队列搞一下就行了。

#include <bits/stdc++.h>
using namespace std;
deque<int>sth;
int n, val;
int main() { 
	scanf("%d",&n);
	for(int i=1;i<=n;++i) { 
		scanf("%d",&val);
		if(i%2) 
			sth.push_back(val);
		else
			sth.push_front(val);
	} 
	if(n % 2 == 0) { 
		while(!sth.empty()) { 
			printf("%d ",sth.front());
			sth.pop_front();
		} 
		return 0;
	} 
	else { 
		while(!sth.empty()) { 
			printf("%d ",sth.back());
			sth.pop_back();
		} 
		return 0;
	} 
	return 0;
} 

D题:
考虑一下什么时候会重复,令 l , r l,r l,r分别为重复出现的元素的两个下标。
当且仅当,你在 [ 1 , l − 1 ] [1,l-1] [1,l1] [ r + 1 , n + 1 ] [r+1,n+1] [r+1,n+1]里面各自选了些东西,且选了 l , r l,r l,r中的一个,且没有在 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r1]中选东西时,会有重复。
容易得出 A n s i = C n + 1 i − C l + n − r i − 1 Ans_i=C_{n+1}^i-C_{l+n-r}^{i-1} Ansi=Cn+1iCl+nri1
注意这里实际上tm有n+1个元素,预处理阶乘的时候要多处理几个位置。
论Wa3发是怎么来的。以后预处理阶乘,统一多处理10个位子得了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1000000007;
long long jc[100010], inv[100010];
long long ans[100010];
int pre[100010];
int n, L, R;
long long fpow(long long di, long long top) {
	long long ret = 1;
	while(top) {
		if(top % 2)
			ret = ret * di % mod;
		di = di * di % mod;
		top /= 2;
	}
	return ret;
}

void init() {
	jc[0] = 1;
	for(int i=1;i<=100005;++i) {
		jc[i] = jc[i-1] * i % mod;
	}
	inv[100005] = fpow(jc[100005], mod - 2);
	for(int i=100005-1;i>=0;--i) {
		inv[i] = inv[i+1] * (i+1) % mod;
	}
	jc[0] = inv[0] = 1;
	return;
}

long long C(int n, int k) {
	if(k > n)
		return 0;
	if(k == n)
		return 1;
	return jc[n] * inv[n - k] % mod * inv[k] % mod;
}

signed main() {
	init();
	int val;
	scanf("%lld", &n);
	for(int i=1;i<=n+1;++i) { 
		scanf("%lld", &val);
		if(!pre[val])
			pre[val] = i;
		else
			L = pre[val], R = i;
	} 
	
	for(int i=1;i<=n+1;++i) { 
		ans[i] = C(n+1, i);
	} 
	
	for(int i=1;i<=n+1;++i) { 
		ans[i] -= C(L + n - R, i - 1);
		ans[i] %= mod, ans[i] += mod, ans[i] %= mod;
		printf("%lld\n", ans[i]);
	} 
	return 0;
} 

E题:
好题。
考虑那个favorite按钮的本质,即,在我们想到到达一个点的时候,我们可以不用选择走路,而是直接花1的代价TP过去。
按照题意模拟一发,开个数组, s u m [ i ] sum[i] sum[i]表示,我们为了到达i点,花费的步数。
比如,我们当前在 3 3 3,想去 11 11 11,那么为了到 4 4 4,花费1步,为了到 5 5 5,花费2步,为了到 6 6 6,花费3步……
不难发现这个是个区间加等差数列。
然后发觉,按favorite也需要花一步诶。
那就把 s u m [ i ] sum[i] sum[i]的含义更改为 “如果favorite在i,我们可以少走多少步”
每次做一个区间加等差数列就好。
两次前缀和。
由于是个环,先做一步拆环成链然后倍长。
前面几次wa是因为没有拆环,不知道为什么有三个点过不去。

#include <bits/stdc++.h>
using namespace std;
long long cha[400010];
long long ccha[400010];
long long sum[400010];
int a[400010];
int n, m;

int main() { 
	scanf("%d%d", &n, &m);
	long long ans = 0, lasp;
	for(int i=1;i<=n;++i) { 
		scanf("%d", &a[i]);
	} 
	lasp = a[1];
	for(int i=2;i<=n;++i) { 
		if(a[i] > lasp) 
			ans += a[i] - lasp;
		else
			ans += a[i] + m - lasp;
		lasp = a[i];
	} 
//	cout<<ans<<endl;
	for(int i=2;i<=n;++i) { 
		int l = a[i-1] + 1, r = a[i];
		if(l == m + 1)
			l = 1;
//		printf("!!! %d %d\n", l, r);
		if(r >= l) { 
			ccha[l]++, ccha[r+1]--, cha[r+1] -= (r - l + 1);
			cha[l] -= 1;
			cha[r+1] += 1;
		} 
		else { 
			int nl = l, nr = r + m;
			ccha[nl]++, ccha[nr+1]--, cha[nr+1] -= (nr - nl + 1);
//
			cha[nl]--, cha[nr+1]++;
		} 
	} 
	for(int i=1;i<=m*2;++i) 
		ccha[i] += ccha[i-1];
	
	for(int i=1;i<=m*2;++i)
		cha[i] += ccha[i];
	
	for(int i=1;i<=m*2;++i) 
		cha[i] += cha[i-1];
	
//	for(int i=1;i<=m*2;++i)
//		printf("%lld ",cha[i]);
//	printf("\n");
	
	for(int i=1;i<=m;++i) 
		sum[i] = cha[i] + cha[i + m];
	
	long long mx = 0;
	for(int i=1;i<=m;++i) { 
		mx = max(mx, sum[i]);
	} 
	cout << ans - mx;
	return 0;
} 
 

赛中甚至没有时间看F题,我居然已经菜成这样了。今天的状态感觉拉的有点厉害qwq。

赛后看了眼F题,果然赛时没看好像没什么影响。
甚至连题解都没看懂。
坑在这里。
https://www.dazhuanlan.com/2019/12/30/5e094e2ed76cb/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值