A:Bestie
题意:
让你求长度为n的数组能使gcd(a1,a2,a3....an)==1的最小成本,对i操作的成本是和下标有关的n−i+1.
操作:
选择第i个元素,让其ai=gcd(ai,i),i从1开始到n;
分析:
题目虽然没给出一个隐藏条件,但是我们能推出来一点就是每个地方最多操作一次就行了,像是a[6]=14;无论你操作多少次都是只能让a[6]=2;
对于 gcd(a,b,c,d),可以等价于gcd(gcd(a,b),gcd(c,d)),再结合一个高中知识:相邻俩个数的gcd等于1,所以最多的成本就是3,其式子等于gcd(gcd(a1,a2,a3....an),gcd(n,n-1))求一下成本小于3的情况就行;
#include<iostream>
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b){
return b? gcd(b,a%b):a;
}
int main(){
ll t;
cin>>t;
while(t--){
ll n,a[50],g=0;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
g=gcd(g,a[i]);
}
if(g==1)cout<<0<<endl;
else if(gcd(g,n)==1)cout<<1<<endl;
else if(gcd(g,n-1)==1)cout<<2<<endl;//操作2次下标为n和一次一样,不考虑
else cout<<3<<endl;
}
}
B:Ugu
题意:
让你把一个n的由0或者1组成的数组操作最小次数变成一个不递减数组:即000,011,111这种
问你最小操作数
操作:
选择下标为i的地方,将i开始往后的元素进行变换,即0变1,1变0.
分析:
已知001这种符合条件,所以我们可以得出要么变换s[i-1]与s[i]不一样的次数,最后结果有三种情况
1.要么在之前能构成前面0,后面有一段是1的情况次。
2.要么都是0。
3.要么都是1(其操作数比第二种小的话和第一种一样,如110变成111和011都是一次)
就是最后面是0的话,操作次数一共是偶数的话你就是在最后面是1的时候多操作了一次,是1反之。
所以我们只需要最后判一下操作数的奇偶是否存在一次多余操作就行。
#include<iostream>
using namespace std;
typedef long long ll;
int main(){
ll t;
cin>>t;
while(t--){
int n;
cin>>n;
string s;
cin>>s;
ll ans=0;
for(int i=1;i<n;i++){
if(s[i-1]!=s[i])ans++;
}
if(ans%2==0&&s[n-1]=='0'){
ans--;
}
if(ans%2==1&&s[n-1]=='1'){
ans--;
}
cout<< (ans > 0? ans:0 )<<endl;//这是特判n为1的情况,懒得用if
}
}
C:Sheikh
题意:
让你求区间中存在的sum(l,r)-xor(l,r)最大,且区间长度最小.
sum(l.r)是l到r的区间和,
xor(l,r)是l下标到r下标的所有元素的异或值。
分析:
有关区间问题9成和前缀和有关,xor的区间异或也能用类似前缀和表示,如1,2,4异或后得到7,2异或4再亦或1一样能得到7,等同于加法结合律。
已知A,B问你A^B和A+B谁大?在二进制中不难看出,假设A=2,B=1,则A^B等于3,A+B也等于3;假设A等于2,B等于2,则A^B等于0,A+B等于4,证明出异或是不进位的加法。
再分析sum(l,r)-xor(l,r)其等同于sum[r]-sum[l-1]-(xor[r]^xor[l-1])再与sum[r]-sum[l]-(xor[r]^xor[l])
相减化简后得出sum[l]-sum[l-1]+(xor[r]^xor[l])-(xor[r]^xor[l-1])等同于a[l]+(xor[r]^xor[l-1]^a[l])-(xor[r]^xor[l-1])再用到之前的推论,得出此式子一定大于等于0,即f(l,r+1)>=f(l,r)是个不递减数组。当a[i]等于0时f(l,r)=f(l,r+1)
最后再在这个数组中运用2分一下求解即可
#include<iostream>
using namespace std;
typedef long long ll;
ll a[100010],sum[100010],_xor[100010];
int x[100010];
int main(){
ll t;
cin>>t;
while(t--){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
_xor[i]=_xor[i-1]^a[i];
}
x[n]=1000000;
for(int i=n-1;i>=1;i--){
if(a[i+1]>0)x[i]=i+1;
else x[i]=x[i+1];
}
ll l,len,ans,lz,rz;
while(q--){
ll L,R;
cin>>L>>R;
ll answ=sum[R]-sum[L-1]-(_xor[R]^_xor[L-1]);
l=L,len=31,ans=R-L+1,lz=L,rz=R;
while(R>l){
if(sum[R]-sum[l-1]-(_xor[R]^_xor[l-1])<answ)break;
int l1=l,r1=R,mid;
while(l1<=r1){
mid=(l1+r1)/2;
if(sum[mid]-sum[l-1]-(_xor[mid]^_xor[l-1])==answ)r1=mid-1;
else l1=mid+1;
}
if(ans>l1-l+1){
ans=l1-l+1;lz=l;rz=l1;
}
if(a[l]>0){
if(len)len--;
else break;
}
l=x[l];
if(l==100000)break;
}
cout<<lz<<" "<<rz<<endl;
}
}
}
D1:Balance
题意:
进行操作n次,符号为“+”的是存入数据,符号“?”的是询问在其是x的倍数关系且不存在在集合中的最小数是多少,让你在“?”时输出其中的数。
分析:
不难发现集合操作中不存在删除(那是D2的题),所以我们每次只需要将询问过的数进行存其,再进行分析在下次询问之前有无新倍数被加进来即可
set进行存数(因为没重复的数据)
map进行快速查找即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
int n;
cin>>n;
set<ll>s;//现在集合中的数
map<ll,ll>ans;//存下之前查询结果,结果比之前结果大或者相等
s.insert(0);
while(n--){
char a;
ll x;
cin>>a>>x;
if(a=='+'){
s.insert(x);
}
else{
ll b=ans[x];
while(s.count(b))b+=x;//count判断是否在s中出现过b;
ans[x]=b;
cout<<b<<endl;
}
}
}