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\n");
else printf("%d\n", 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 \bmod 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\n", 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("\n");
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("\n");
return 0;
}