A. Bestie
题目链接:A. Bestie
样例:
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
output:
0
1
2
2
1
3
3
0
1
题意:
给定一个长度为n的数组 a,想令 gcd( a[1]~a[n] )=1 ,即只要a数组中有两个数互质即可。有一种操作为 令a[i]=gcd(i,a[i]) 代价为 n-i+1, 代价最小为多少?
思路:
代价为 n-i+1,即数组后面的数代价小,前面代价大,所以优先考虑变后面。我们知道连续的两个自然数的gcd为1,如(1,2)(5,6) ,那么代表着只需要动后面两个数即可。这样代价范围仅为0~3
代价为0,即数组中本来存在互质数
代价为1,操作一次a[n]=gcd(a[n],n)
代价为2,操作一次a[n-1]=gcd(a[n-1],n-1)
那么代价最大为3,即 a[n]=gcd(a[n],n) ,a[n-1]=gcd(a[n-1],n-1)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=25;
int t,n;
int a[MAXN];
int main()
{
ios::sync_with_stdio(false);
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int t=a[1],t1;
for(int i=2;i<=n;i++) t=__gcd(t,a[i]);
if(t==1)
{
cout<<0<<endl;
continue;
}
t1=a[n];
a[n]=__gcd(a[n],n);
t=a[1];
for(int i=2;i<=n;i++) t=__gcd(t,a[i]);
if(t==1)
{
cout<<1<<endl;
continue;
}
a[n]=t1;
a[n-1]=__gcd(a[n-1],n-1);
t=a[1];
for(int i=2;i<=n;i++) t=__gcd(t,a[i]);
if(t==1)
cout<<2<<endl;
else
cout<<3<<endl;
}
return 0;
}
B. Ugu
题目链接:B. Ugu
样例
input:
8
1
1
2
10
3
101
4
1100
5
11001
6
100010
10
0000110000
7
0101010
output:
0
1
2
1
2
3
1
5
题意:
给定一个长度为n的 01串 s,有一种操作为选择一个下标 i ,让i之后的01全部翻转。问多少次操作可以使s变为递增串。
思路:
从头开始0都是合法的,但如果遇到了1,若1后面还有0,如000011100111中红色部分就是不合法的,所以必须在遇到1的块中的最后一个1,即绿色1地方进行翻转,那么后面部分的0变1,1变0;
串变为0000111 11 000,此时,原本的0变为1合法了,但原本的1变为0不合法了。
故每次遇到0的块,进行翻转,0的块变为1后,0的块的后面的1块又变为0了,所以每个块都进行一次翻转,所以从头开始遇到第一个 1时,计算1 之后的0 和 1的块数,连续的1或者连续的0组成一个块。这个块数即为答案。
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+1;
int T,n;
char s[MAXN];
int main()
{
cin>>T;
while(T--)
{
cin>>n;
scanf("%s",s+1);
int cnt=0,i;
for(i=1;i<=n;i++)
if(s[i]=='1') break;
char pre='1';
for(i;i<=n;i++)
{
if(s[i]!=pre) cnt++;
pre=s[i];
}
cout<<cnt<<endl;
}
return 0;
}
D1. Balance (Easy version)
题目链接:D1. Balance (Easy version)
样例 1
input:
15
+ 1
+ 2
? 1
+ 4
? 2
+ 6
? 3
+ 7
+ 8
? 1
? 2
+ 5
? 1
+ 1000000000000000000
? 1000000000000000000
output:
3
6
3
3
10
3
2000000000000000000
样例 2
input:
6
+ 100
? 100
+ 200
? 100
+ 50
? 50
output:
200
300
150
题意:
给定一个集合 T ,初始 T 中只有一个元素0,有两种操作,一种操作为‘+ x’,代表向集合T中添加一个元素 x,一种操作为 ‘? k’, 代表询问最小的不在集合T中的最小的k的倍数是多少?由于x的范围为1~1e18,故答案最大为2e18,用long long可以存下。
思路:
map<long long,long long>mp,mp1
其中mp记录集合T。
mp1记录询问过的集合,mp1->first记录的是已经询问过的k,mp1->second 记录上次询问的 k 得出的最小的不在集合 T 中最小k的倍数是多少。
这样对于每次询问的k,如果mp1中记录过 k已经询问过,则便从mp1中记录的位置开始寻找;否则就从k开始,每次增加k开始寻找。
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+1;
typedef long long ll;
int T;
ll t;
map<ll,ll> mp,mp1;
int main()
{
ios::sync_with_stdio(false);
cin>>T;
char op;
while(T--)
{
cin>>op>>t;
if(op=='+')
{
mp[t]=1;
}
else
{
ll i;
if(mp1.count(t)==0)
{
for(i=t;;i+=t)
{
if(mp.count(i)==0)
{
cout<<i<<endl;
break;
}
}
mp1[t]=i;
}
else
{
for(i=mp1[t];;i+=t)
{
if(mp.count(i)==0)
{
cout<<i<<endl;
break;
}
}
mp1[t]=i;
}
}
}
return 0;
}
D2. Balance (Hard version)(可能被hack,具体见评论)
题目链接:D2. Balance (Hard version)
题意:
相比easy vesion多了一个移除操作。
给定一个集合 T ,初始 T 中只有一个元素0,有三种操作
‘+ x’,代表向集合T中添加一个元素 x
‘- x’, 代表从集合T中移除一个元素 x
‘? k’, 代表询问最小的不在集合T中的最小的k的倍数是多少?
由于x的范围为1~1e18,故答案最大为2e18,用long long可以存下。
思路:
map<long long,long long>mp,mp1,mp2
其中mp记录集合T.
mp1记录询问过的k的集合,mp1->first记录的是已经询问过的k,mp1->second 记录上次询问的 k 得出的最小的不在集合 T 中最小k的倍数是多少。
mp2记录的为两次询问之间删除的x的集合,mp2[x]==1即代表x被删不在集合T中。
这样对于每次询问的k,mp1中未记录k则代表第一次问,则从k开始每次自加k寻找。
若以前询问过,则mp1中记录的位置可能失效,因为或许有更小的k的倍数被从T中移出了。
map是有序的,我们想判断被移除的数中是否有k的倍数,k的倍数则一定比k大;所以从mp2中第一个大于等于k的位置(用map的lower_bound寻找这个位置)开始寻找有没有比mp1中记录的位置更小的k的倍数,有则输出,没有则从上次记录的位置开始寻找,同时不断更新mp1的记录位置。
可行性分析:
设在某次询问前 加操作为n1次,减操作为n2次,询问操作n3次,这三种操作是相互制约的关系,n1+n2+n3至多为2e5,某个操作多了其他操作次数必然少。
对于某个数,从mp中查询为log(n1-n2),mp1中查询为log(n3),mp2中查询为log(n2),时间很小 相当于无影响。
最卡时间的方法一定为询问1
对于每个k,第一次询问的时间复杂度为O(n1/k)
其他情况,设在上次询问k 和本次询问k 期间 集合T中增加了 n个数。
设mp2中比k大的数有t个,以后的查询则至多为O(log(n2-t)+t)+O(n/k)
对于3s的限制很轻松
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+1;
typedef long long ll;
int T;
long long t;
map<ll,ll> mp,mp1,mp2;
int main()
{
ios::sync_with_stdio(false);
cin>>T;
char op;
int cnt=0,pre=-1;
while(T--)
{
cin>>op>>t;
bool flag=0;
if(op=='+')
{
mp[t]=1;
if(mp2.count(t)!=0) mp2.erase(t);
cnt=1;
}
else if(op=='-')
{
mp.erase(t);
mp2[t]=1;
cnt=1;
}
else
{
ll i;
if(pre==t&&cnt==0)
{
cout<<mp1[t]<<endl;
continue;
}
cnt=0;
if(mp1.count(t)==0)
{
for(i=t;;i+=t)
{
if(mp.count(i)==0)
{
cout<<i<<endl;
break;
}
}
mp1[t]=i;
}
else
{
bool f=0;
for(auto iter=mp2.lower_bound(t);iter != mp2.end(); iter++)
{
if(iter->first>mp1[t]) break;
if((iter->first)%t==0)
{
f=1;
cout<<iter->first<<endl;
break;
}
}
if(f) continue;
for(i=mp1[t];;i+=t)
{
if(mp.count(i)==0)
{
cout<<i<<endl;
break;
}
}
mp1[t]=i;
}
}
pre=t;
}
return 0;
}