AtCoder ARC 125 部分题解

A - Dial Up

找到最近的与第一个位置不同的数字的位置,第一次转换需要这个距离的代价,之后的转换只需要 (1) 的代价左右切换即可

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

const int maxn = 200010;
const int INF = 1000000007;

int n, m;
int a[maxn], b[maxn];

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	n = read(), m = read();
	
	int l = INF, r = INF;
	for(int i = 1 ; i <= n ; ++i) {
		a[i] = read();
		if(i > 1 && a[i] != a[1]) r = min(r, i-1);
	}
	for(int i = 1 ; i <= m ; ++i) b[i] = read();
	
	for(int i = n ; i >= 2 ; --i) if(a[i] != a[1]){
		l = n-i+1; break;
	}

	int cur = a[1], ans = 0, flag = 0;
	
	for(int i = 1 ; i <= m ; ++i){
		if(cur != b[i]){
			ans += (flag == 0) ? min(l, r) : 1;
			cur ^= 1;
			flag = 1;
		}
		++ans;
	}
	
	if(ans >= INF) printf("-1
");
	else printf("%d
", ans);
	
	return 0;
}

B - Squares

注意移项后可以变成平方差公式,分解后变为 ((x+t)(x-t)=y),令 (p=(x+t),q=(x-t)),则原问题转化成计数 ((p,q)) 对的数量,满足

  • (q <= p)
  • (frac{p+q}{2}=x)
  • (pq leq n)
  • (q equiv p mod 2)
  • (q leq p leq frac{n}{q})
    枚举 (q) 即可,(q) 不超过 (sqrt n)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int M = 998244353; 

ll n;

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	n = read();
	int ans = 0;
	for(int i = 1 ; i <= (int)sqrt(n+0.5) ; ++i){
		ll l = i, r = n / i;
		if((r-l+1)&1) {
			ll len = (r-l)/2;
			ans = (ans + len % M + 1) % M;
		} else{
			ll len = (r-l+1)/2;
			ans = (ans + len % M) % M;
		} 
	}
	printf("%d
", ans);
	return 0;
}

C - LIS to Original Sequence

题意:

给定一个排列,给定其中的一个最长上升子序列,求满足条件的字典序最小的排列

题解:

如果要求最长上升子序列长度为 (k),则序列中一定有 (k) 个连续的的下降段,这样保证同一下降段中的数不会形成上升序列,不同下降段中的数形成上升序列,且长度为 (k),贪心构造即可

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

const int maxn = 200010;
const int INF = 1000000007;

int n, k;
int a[maxn]; 
int vis[maxn];

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	n = read(), k = read();
	for(int i = 1 ; i <= k ; ++i) a[i] = read(), vis[a[i]] = 1;
	
	int cur = 1;
	while(vis[cur]) ++cur;
	for(int i = 1 ; i < k ; ++i){
		printf("%d ", a[i]);
		if(cur < a[i]) {
			printf("%d ", cur);
			cur++;
			while(vis[cur]) ++cur;	
		}
	}
	for(int i = n ; i > max(a[k], cur) ; --i){
		if(!vis[i]) printf("%d ", i);
	}
	printf("%d ", a[k]);
	for(int i = a[k]-1 ; i >= cur ; --i){
		if(!vis[i]) printf("%d ", i);
	} printf("
");
	return 0;
}

D - Unique Subsequence

题意:

给定一个序列,求有多少个子序列在该序列中只出现一次

题解:

(dp[i]) 表示第 (i) 个元素必选,序列前 (i) 个元素中,子序列只出现一次的数量,则若 (f[i]) 能转移到 (f[j]),要满足 ((i,j)) 区间中不出现 (a[i],a[j]),否则生成的子序列不唯一

所以 (f[i]) 统计的答案即为 ((pre[i],i-1)) 中不含 (a[j])(f[j]) 之和,可以使用树状数组维护,只维护当前每个值最后一个位置的 (f),因为每个值只有最后一个位置会对答案产生贡献,统计答案只统计每个值最后一个位置的 (f)

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

const int maxn = 200010;
const int INF = 1000000007;

int n, k;
int a[maxn]; 
int vis[maxn];

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	n = read(), k = read();
	for(int i = 1 ; i <= k ; ++i) a[i] = read(), vis[a[i]] = 1;
	
	int cur = 1;
	while(vis[cur]) ++cur;
	for(int i = 1 ; i < k ; ++i){
		printf("%d ", a[i]);
		if(cur < a[i]) {
			printf("%d ", cur);
			cur++;
			while(vis[cur]) ++cur;	
		}
	}
	for(int i = n ; i > max(a[k], cur) ; --i){
		if(!vis[i]) printf("%d ", i);
	}
	printf("%d ", a[k]);
	for(int i = a[k]-1 ; i >= cur ; --i){
		if(!vis[i]) printf("%d ", i);
	} printf("
");
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/15257548.html