上大分~怎么说上大分
A. Bestie
题意
给一个数组,可以进行如下操作:选择一个ai,使得ai=gcd(ai,i),每次操作的花费为n-i+1,问使得数组gcd为1的最小花费
思路
开幕雷击,卡了一会才写出来,众所周知,两个相邻自然数的gcd为1,而数组只要有两个数的gcd为1,整个数组的gcd就为1,考虑花费最小,不难想到直接修改最后两个数,分情况判断不改,改一个,改两个即可。
代码
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n,res=0;
cin>>n;
vector<int>a(n+1);
int t;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(i==1) t=a[1];
else t=__gcd(a[i],t);
}
if(t==1)
{
puts("0");
return;
}
if(__gcd(t,n)==1)
{
puts("1");
return ;
}
if(__gcd(t,n-1)==1)
{
puts("2");
return ;
}
puts("3");
return ;
}
int main()
{
int tt;
cin>>tt;
while(tt--)
solve();
}
B. Ugu
题意
给一个只由01构成的字符串s,每次操作选择一个位置i,使得从si开始往后的所有01反转。问使得字符串不减的最小操作次数。
思路
直接全搞成0,遇见1反转,后面的0会变成1,再进行反转,后面的0又会变成1,举个例子:001110110->000001001-> 000000110->000000001->000000000
所以相当于是从第一个1开始,每次从0变1或者从1变0都要进行一次操作
代码
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin>>n;
string s;
cin>>s;
int res=0,flag=0,pre='1';
for(int i=0;i<n;i++)
{
if(s[i]=='1')
flag=1;
if(s[i]!=pre&&flag)
{
res++;
pre=s[i];
}
}
cout<<res<<endl;
return ;
}
int main()
{
int tt;
cin>>tt;
while(tt--)
solve();
}
C1. Sheikh (Easy version)
题意
给一个数组,在这个数组中找一个区间l~r,使得sum [l , r] - xor [l , r]最大,且区间长度最小,求l,r
思路
显然当区间变小,答案不会更优,整个区间的最大值即为sum[1,n] - xor[1,n] ,接下来二分答案确定最小区间长度,check时枚举起点,通过前缀和和前缀异或和O1查询即可。
代码
#include<bits/stdc++.h>
#define ll long long
const int maxn=2e5+10;
using namespace std;
ll sum[maxn],xr[maxn],a[maxn];
ll MAX;
int L,R;
int n,q;
int check(int len)
{
ll t1,t2;
for(int i=1;i+len-1<=n;i++)
{
t1=i,t2=i+len-1;
ll info1=sum[i+len-1] - sum[i-1];
ll info2=xr[i+len-1] ^ xr[i-1];
if(info1 - info2 == MAX)
return i;
}
return 0;
}
void solve()
{
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum[i] = sum[i-1] + a[i];
xr[i] = xr[i-1] ^ a[i];
}
cin>>L>>R;
int r=n,l=1;
MAX=sum[R]-xr[R];
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<check(l)<<" "<<check(l)+l-1<<endl;
return ;
}
int main()
{
int tt;
cin>>tt;
while(tt--)
solve();
}
C2看不懂题解 有缘再补(
D1. Balance (Easy version)
题意
一开始给定一个没有元素的集合,给两种操作,一种是在集合中插入一个元素x,一种是查询元素中最小的没有出现的x的倍数。
思路
记忆化暴力() 出在d1多少有点水,开一个set s,记录插入了哪些数,记录开一个map,mp[x]表示查询x的答案,下次再查询x看mp[x]是否出现在集合中,没出现直接输出,出现了接着暴力找答案并更新map
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
void solve()
{
int q;
cin>>q;
set<ll>s;
map<ll,ll>p;
s.insert(0);
while(q--)
{
char op; ll x;
cin>>op>>x;
if(op=='+')
s.insert(x);
else
{
ll t=x;
ll temp=p[t];
if(temp!=0&&!s.count(p[t]))
{
cout<<p[t]<<'\n';
continue;
}
if(temp!=0) t=p[t];
while(s.count(t))
{
t+=x;
}
p[x]=t;
cout<<p[x]<<'\n';
}
}
return ;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
}
D2. Balance (Hard version)
题目链接
题意
和easy版本相比增加了一种操作:从集合中删除一个数,别的不变
思路
再开一个set del记录被删除的数,每次插入,如果del中有,就从del中删掉x,每次删除如果s中有,就从s中删掉该元素。每次查询在del中二分找第一个大于x的数输出,如果找到的数大于mp[x]了,就从mp[x]开始暴力查询结果。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
void solve()
{
int q;
cin>>q;
set<ll>s,del;
map<ll,ll>p;
s.insert(0);
while(q--)
{
char op; ll x;
cin>>op>>x;
if(op=='+')
{
if(del.count(x)) del.erase(x);
s.insert(x);
}
else if(op=='-')
{
del.insert(x);
s.erase(x);
}
else
{
ll t=x;
if(!p[t])
{
while(s.count(t))
t+=x;
p[x]=t;
cout<<p[x]<<'\n';
}
else
{
auto i = del.lower_bound(x);
bool flag=0;
for(i;i!=del.end();i++)
{
if(*i>p[x]) break;
if(*i % x == 0)
{
cout<<*i<<endl;
flag=1;
break;
}
}
if(!flag)
{
t = p[x];
while(s.count(t))
t+=x;
cout<<t<<endl;
p[x] = t;
}
}
}
}
return ;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
}