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;
}