A. Bestie
Sample input
9
1
1
1
2
2
2 4
3
3 6 9
4
5 10 15 20
5
120 60 80 40 80
6
150 90 180 120 60 30
6
2 4 6 9 12 18
6
30 60 90 120 125 125
Sample output
0
1
2
2
1
3
3
0
1
题意:
给你一个长度为n的数组,你可以进行一种操作,将第i个数变为gcd(i,a[i]),这一步操作的花费为n-i+1,然后问你最少的操作花费将这个序列的gcd变成1。
思路:
因为这个n最大不超过20,将最后两个数都进行一步操作就能将整个序列的gcd变成1,这个花费是3,所以说只需要找那种只变一个数就能将gcd变成1的答案的最小值和这个3取最小就行,如果本来这个序列的gcd就是1的话那直接输出0,下面看代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
int a[22],b[22];
void solve(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
int x = a[1];
for(int i=2;i<=n;i++) x = __gcd(x,a[i]);
if(x == 1){
puts("0");
return;
}
int ans = n>=2?3:1;
for(int i=n;i>=1;i--){
memcpy(b,a,sizeof b);
b[i] = __gcd(b[i],i);
int x = b[1];
for(int i=2;i<=n;i++) x = __gcd(x,b[i]);
if(x == 1) ans = min(ans,n-i+1);
}
printf("%d\n",ans);
}
int main(){
int _;
scanf("%d",&_);
while(_--) solve();
return 0;
}
B. Ugu
Sample input
8
1
1
2
10
3
101
4
1100
5
11001
6
100010
10
0000110000
7
0101010
Sample output
0
1
2
1
2
3
1
5
题意:
给你一个长度为n的01串,你现在可以进行一步操作,选定一个位置i,将(i,n)这个序列中的所有元素都取反,现在问你最少需要多少步这种操作能将这个数组变成不降的
思路:
就是暴力,如果当前位是1但是后面有0的话那就翻转,实现方式的话就是记录一下反转了奇数次还是偶数次就行,下面看代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
char s[N];
int sum[N];
void solve(){
int n,pos = 0;
scanf("%d%s",&n,s+1);
for(int i=1;i<=n;i++){
s[i] = s[i] - '0';
sum[i] = sum[i-1] + s[i];
}
int cnt = 0,ans = 0;
s[0] = 0;
for(int i=1;i<=n;i++){
if((s[i]^cnt) && ((!cnt && sum[n] - sum[i-1] != n - i + 1) || (cnt && sum[n] - sum[i-1] != 0))){
ans ++;
cnt ^= 1;
}
}
printf("%d\n",ans);
}
int main(){
int _;
scanf("%d",&_);
while(_--) solve();
return 0;
}
C1. Sheikh (Easy version)
Sample input
6
1 1
0
1 1
2 1
5 10
1 2
3 1
0 2 4
1 3
4 1
0 12 8 3
1 4
5 1
21 32 32 32 10
1 5
7 1
0 1 0 1 0 1 0
1 7
Sample output
1 1
1 1
1 1
2 3
2 3
2 4
题意:
给你一个长度为n的区间,有q组询问,每组询问给你一个L,R,问在(L,R)内哪一段的区间和减去区间异或和最大,若有多个最大值则输出最短的那个区间,C1与C2的区别只是q=1,其中L1 = 1,R1 = n。
思路:
有一个性质,就是一段区间的加和一定小于等于一个区间的异或和,所以说一整个区间(1,n)的的差值就是最大的差值,最后左右两边向中间缩就行,这个过程可以用双指针来维护,下面请看代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200010;
int a[N];
long long sum1[N],sum2[N];
void solve(){
int n,q;
scanf("%lld%lld",&n,&q);
int l,r;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum1[i] = sum1[i-1] + a[i];
sum2[i] = sum2[i-1] ^ a[i];
}
scanf("%lld%lld",&l,&r);
int ans=0;
ans=(sum1[r] - sum1[l-1] - (sum2[r] ^ sum2[l-1]));
int j=1;
int ansl=0;
int ansr=1e9;
for(int i=1;i<=n;i++)
{
j=max(j,i);
while(j<n && (sum1[j] - sum1[i-1] - (sum2[j] ^ sum2[i-1]))<ans)
j++;
if(j-i+1<ansr-ansl+1 && (sum1[j] - sum1[i-1] - (sum2[j] ^ sum2[i-1])==ans))
{
ansl=i;
ansr=j;
}
}
printf("%lld %lld\n",ansl,ansr);
}
signed main(){
int _;
scanf("%lld",&_);
while(_--) solve();
return 0;
}
D1. Balance (Easy version)
Sample input1
15
- 1
- 2
? 1 - 4
? 2 - 6
? 3 - 7
- 8
? 1
? 2 - 5
? 1 - 1000000000000000000
? 1000000000000000000
Sample output1
3
6
3
3
10
3
2000000000000000000
Sample input2
6
- 100
? 100 - 200
? 100 - 50
? 50
Sample output2
200
300
150
题意:
一开始有一个空集合,给你n组询问,每一组都会进行+操作或者是?操作,分别是向集合内加一个数和询问x的最小没出现过的倍数是多少
思路:
如果暴力的来看,所有询问的暴力操作加起来就是nlogn的复杂度,因为对2的询问就是n/2,对3的询问是n/3,… ,所以说所有书的加起来就是n*ln(n),下面请看代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
void solve(){
int m;
map<long long,long long> mp,st;
scanf("%d",&m);
while(m--){
long long t;
char s[2];
scanf("%s%lld",s,&t);
if(*s == '+'){
st[t] = 1;
}
else{
for(long long i=max(mp[t],1ll*t),cnt=0;cnt<N;cnt++,i+=t){
if(!st[i]){
mp[t] = i;
printf("%lld\n",i);
break;
}
}
}
}
}
int main(){
int _=1;
while(_--) solve();
return 0;
}
D2. Balance (Hard version)
Sample input1
18
- 1
- 2
? 1 - 4
? 2 - 6
? 3 - 7
- 8
? 1
? 2 - 5
? 1 - 1000000000000000000
? 1000000000000000000
- 4
? 1
? 2
Sample output1
3
6
3
3
10
3
2000000000000000000
3
4
Sample input2
10
- 100
? 100 - 200
? 100
- 100
? 100
- 50
? 50
- 50
? 50
Sample ouput2
200
300
100
100
50
题意:
跟上个题的区别就是一个remove,从集合中删除一个数。
思路:
要先弄清楚这个小小的remove它的影响是什么,会发现它只会对在remove之前询问过的因子和remove之后询问的因子产生结果不相同的影响,通俗一点来讲吧,假如说当前remove的数是x,他之前的某个因子 t 询问过,已经把这个x给遍历过去了,那么说这个x就是 t 的绊脚石,因为他上次维护的右端点是tk 把,这个tk是一定大于这个x的,相当于是出现了一个1111011111这类的情况,所以说咱们只需要将每个因子的绊脚石给找出来,下次询问的时候先查绊脚石有没有在集合内,然后再跟D1一样就行,下面请看代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
map<long long,set<long long> > mp;
map<long long,vector<long long> > e;
map<long long,long long> mpp,st;
void solve(){
int m;
scanf("%d",&m);
while(m--){
char s[2];
long long t;
scanf("%s%lld",s,&t);
if(*s == '+'){
st[t] = 1;
}
else if(*s == '-'){
st[t] = 0;
for(int i=0;i<e[t].size();i++) mp[e[t][i]].insert(t);
}
else{
bool flag = false;
set<long long> q;
for(auto x : mp[t]){
if(!st[x]){
printf("%lld\n",x);
flag = true;
break;
}
else q.insert(x);
}
for(auto x : q) mp[t].erase(x);
if(flag) continue;
for(long long i=max(mpp[t],1ll*t),cnt=0;cnt<N;cnt++,i+=t){
if(!st[i]){
mpp[t] = i;
printf("%lld\n",i);
break;
}
e[i].push_back(t);
}
}
}
}
int main(){
int _=1;
while(_--) solve();
return 0;
}