1748C Zero-Sum Prefixes
题意:给出n个数,其中为0的数可以改变成任意数
要使:
∑
i
=
1
j
a
i
=
0
(
1
<
=
j
<
=
n
)
\sum_{i=1}^{j} a_i =0 (1<=j<=n)
i=1∑jai=0(1<=j<=n) 求 满足这样的 j 最多有多少个
思路:当 ai 为0时,它对于前 i 位的前缀和无影响,从后往前枚举,记录0和上一个0之间前缀和最多的一个数,此时该位0直接改为此数相反值。枚举完答案还要加上mp[0]
void solve()
{
int n;
cin>>n;
vector<ll> a(n+1),sum(n+1);
for (int i=1;i<=n;i++)
cin>>a[i],sum[i]=sum[i-1]+a[i];
map<ll,int> mp;
int ans=0;
for (int i=n;i>=1;i--)
{
mp[sum[i]]++;
if (!a[i])
{
int maxn=0;
for (auto j:mp)
maxn=max(maxn,j.second);
ans+=maxn;
mp.clear();
}
}
cout<<ans+mp[0]<<"\n";
}
1748D ConstructOR
题意:给出a,b,d,求x使得:
a|x,b|x都能被d整除
思路:
以二进制来枚举d的倍数,使得a|x与b|x都等于此倍数
找a,b,d二进制出现1的最低位为minn,如果minn由a或b产生,则无解(无法找到d的倍数在minn位为1)
对于a,b二进制第i位上有1,则x就需加上d<<(i-minn)
void solve()
{
int minn;
ll a,b,d;
cin>>a>>b>>d;
a=(a|b);
for (int i=0;;i++)
{
if (((a>>i)&1)&&((d>>i)&1)==0)
{
cout<<"-1\n";
return;
}
if ((d>>i)&1)
{
minn=i;
break;
}
}
ll ans=d;
for (int i=0;i<=60;i++)
{
if (((a>>i)&1)&&((ans>>i)&1)==0)
ans+=(d<<(i-minn));
}
cout<<ans<<"\n";
}
1747C Swap Game
题意:a和b玩游戏,给出n个数,当轮到某一个人:
- a1-=1;
- 选择ai和a1交换
当轮到某个人时 a1=0则输
思路:
当某轮存在a1 …0… 则先手必赢–>存在a1=1… 则先手必输
n个数中只有最小的两个数有用,双方都想让对方的a1尽可能的小
于是出现以下策略:
到自己时,选择的i一定是2~n中最小的ai,值为minn
所以当a0>=minn,先手必赢;a0<minn,先手必输
1746C Permutation Operations
题意:给n个数,执行n次操作,第i次操作选择j,使ak+=i(j<=k<=n)
找出某种方案使得该数组中逆序数最少
思路:对于每次操作,选择j,只会改变aj-1与aj的差值,于是求出差分数组,对其中的负数,每次加给最大的负数
struct node
{
int pos,val;
};
bool cmp(node x,node y)
{
return x.val>y.val;
}
void solve()
{
int n;
cin>>n;
vector<int> a(100005);
vector<node> b;
for (int i=1;i<=n;i++)
{
cin>>a[i];
if (a[i]-a[i-1]<0)
b.push_back({i,a[i]-a[i-1]});
}
sort(b.begin(),b.end(),cmp);
int i=1;
for (int j=0;j<b.size();j++)
{
while (b[j].val<0&&i<=n)
{
b[j].val+=i;
i++;
cout<<b[j].pos<<" ";
}
if (i==n+1)
break;
}
while (i++<=n)
cout<<n<<" ";
cout<<"\n";
}
1742G Orray
题意:给出n个数ai,bi=a1|a2…|ai,对a排序,使得b字典序最大
思路:b1=a1,所以第一个数必定ai中最大的数。之后枚举到i时,只需找到bi-1的最高位0,枚举a中未使用且这一位为1的数(预处理存入数组),并找到其中对bi贡献最大的数(使bi-bi-1最大)
void solve()
{
int n,pos,maxn=0;
cin>>n;
vector<bool> sym(n+1);//标记是否使用过
vector<int> a(n+1);
vector<vector<int>> good(32);//第i位为1的数
for (int i=1;i<=n;i++)
{
cin>>a[i];
int temp=a[i];
for (int j=0;temp;j++)
{
if (temp%2==1)
good[j].push_back(i);//预处理
temp/=2;
}
if (maxn<a[i])
{
pos=i;
maxn=a[i];
}
}
sym[pos]=1;
cout<<maxn<<" "; //输出b[1]
for (int i=30;i>=0;i--)
{
if (((maxn>>i)&1)==0)//最高位0
{
int val=0;
for (auto j:good[i])//枚举第i位为1的数
{
if (sym[j])continue;//已使用的跳过
if (val<(maxn|a[j])-maxn)
{
val=(maxn|a[j])-maxn;
pos=j;
}
}
if (sym[pos])continue;
maxn|=a[pos];
sym[pos]=1;
cout<<a[pos]<<" ";
}
}
for (int i=1;i<=n;i++)//后续答案已不会改变,输出未选的
{
if (!sym[i])
cout<<a[i]<<" ";
}
cout<<"\n";
}
1742F Smaller
题意:给两个字符串s=“a”,t=“a”,每次操作:
1 k x,给s加上k个x
2 k x,给t加上k个y
每次操作完可排序,若存在 s < t,输出YES;反之NO
思路:
贪心+模拟,每次统计s,t中26个字母的个数,s从a开始找最小字母,t从z开始找最大字母。
YES: a的最小字母 < b的最大字母;
NO: a的最小字母 > b的最大字母
l,r继续移动:
a的最小与b的最大为同一字母
特例:
a的最小与b的最大为同一字母 a只有该字母或b只有该字母(具体见代码)
void solve()
{
int q;
cin>>q;
vector<ll> sum1(30),sum2(30);
sum1[0]++;
sum2[0]++;
while (q--)
{
ll minn=1e18;
for (int i=0;i<=25;i++)
minn=min(minn,min(sum1[i],sum2[i]));
for (int i=0;i<=25;i++)
sum1[i]-=minn,sum2[i]-=minn;
int d,k;
string x;
cin>>d>>k>>x;
int len=x.length();
if (d==1)
{
for (int i=0;i<len;i++)
sum1[x[i]-'a']+=k;
}
else
{
for (int i=0;i<len;i++)
sum2[x[i]-'a']+=k;
}
for (int l=0,r=25;l<=26&&r>=-1;)
{
while (!sum1[l]&&l<=25){l++;}
while (!sum2[r]&&r>=0){r--;}
if (r==-1||(r==-1&&l==26))
{
cout<<"NO\n";
break;
}
if (l==26)
{
cout<<"YES\n";
break;
}
if (l<r)
{
cout<<"YES\n";
break;
}
if (l>r)
{
cout<<"NO\n";
break;
}
if (l==r)
{
if (sum1[l]==sum2[r])
l++,r--;
else if (sum1[l]>sum2[r])
r--;
else
l++;
}
}
}
}
1742D Coprime
题意:给n个数,找i,j使ai与aj互质,求max(i+j),若不存在i,j,输出-1
思路:由数据范围可知,当数据很大时,数组中有很多重复的数,于是可以把每个数的最大位置存下来,之后1000*1000枚举即可
1742E Scuza
题意:给n个数,q次询问,每次询问给一个x,只要ai<=x,i++,问给出的x可以到达最大的i的前缀和为多少
思路:直接求出到达i最小需要的值,之后二分查找upper_bound第一个大于x的位置,输出前缀和即可
1753B Factorial Divisibility
题意:给出n个数ai和一个x,判断是否
∑
1
n
a
i
m
o
d
x
=
=
0
\sum_{1}^{n} a_i mod x==0
1∑naimodx==0
思路:考虑每一个
a
i
!
a_{i}!
ai!,当
a
i
>
=
x
a_{i}>=x
ai>=x时,一定能被
x
!
x!
x!整除;
对于
a
i
<
x
a_{i}<x
ai<x,我们只需将多个
a
i
!
a_{i}!
ai!合并为
(
a
i
+
1
)
!
(a_{i}+1)!
(ai+1)!,一直合并到最大,若此时还存在
a
i
<
x
a_{i}<x
ai<x,则为NO,反之为YES
void solve()
{
int n,x;
cin>>n>>x;
vector<int> sum(500005);
for (int i=1;i<=n;i++)
{
int a;
cin>>a;
if (a<=x-1)sum[a]++;
}
for (int i=1;i<=x-1;i++)
{
if (sum[i]>=i+1)
{
sum[i+1]+=sum[i]/(i+1);
sum[i]-=sum[i]/(i+1)*(i+1);
}
if (sum[i])
{
cout<<"No";
return;
}
}
cout<<"Yes";
}
1738B Prefix Sum Addicts
题意:给定一个n,k,给定k个数
a
i
a_{i}
ai 表示为前n-k+i个数的前缀和,问是否能构造出满足前缀和的非降序原数组
思路:先看n-k+1这个数的前缀和,求出最小的
a
n
−
k
+
1
a_{n-k+1}
an−k+1,然后看是否满足
a
n
−
k
+
1
<
=
a
n
−
k
+
2
a_{n-k+1}<=a_{n-k+2}
an−k+1<=an−k+2,不满足输出NO
求出后k-1个原数组,判断是否单调递增
1732D1 Balance (Easy version)
题意:给定一个集合{0},进行q次操作
+ x,在集合中加入x
? x,找到一个不存在集合中且能被x整除的最小数
思路:
直接用map mp1标记集合中有哪些数,并用map mp2储存某个数满足题目的最小数(优化时间,减少查询,因为集合中的数变多,最小数要么不变要么变大)
map <ll,ll> mp1,mp2;
void solve()
{
char ch;
ll x;
cin>>ch>>x;
if (ch=='+')
mp1[x]=1;
if (ch=='?')
{
while (mp1[mp2[x]+x]==1)
{
mp2[x]+=x;
}
cout<<mp2[x]+x<<"\n";
}
}
1730B Meeting on the Line
题意:在一坐标轴上给出n个人的位置
x
i
x_{i}
xi,每个人有一个穿衣服的时间
t
i
t_{i}
ti,求某一个点使得所有人到该点的时间最小化,误差允许在1e-6以内
思路一:二分答案,找最小的时间,check满足时更新ans(控制精度防T)
const int N=2e5+5;
struct node
{
int x,dtime;
}a[N];
double ans;
int n;
bool check(double x)
{
double l=0,r=1e9;
for (int i=1;i<=n;i++)
{
l=max(l,a[i].x-x+a[i].dtime);
r=min(r,a[i].x+x-a[i].dtime);
if (l>r)
return 0;
}
ans=l;
return 1;
}
void solve()
{
memset(a,0,sizeof(a));
double l=0,r=1e9;
cin>>n;
for (int i=1;i<=n;i++)
cin>>a[i].x;
for (int i=1;i<=n;i++)
cin>>a[i].dtime;
while (r-l>0.0000001)
{
double mid=(l+r)/2;
if (check(mid))
r=mid;
else
l=mid;
}
cout<<fixed<<setprecision(6)<<ans<<"\n";
}
思路二:待补
1728C Digital Logarithm
题意:给出两个数组,求最少花多少次操作使得两数组相同。
对一个数操作,返回该数的长度
思路:相同的数全部排除,统计a,b中各数的个数,在a中+,在b中-(抵消相同的数)
从最大的数向最小的数枚举,最大的数必须转换成小的(如果a,b中一直没有相同的最终转化为1),对答案产生贡献
void solve()
{
int n;
cin>>n;
map<int,int,greater<int>> sum;
for (int i=1;i<=2*n;i++)
{
int x;
cin>>x;
if (i<n+1) sum[x]++;
else sum[x]--;
}
int ans=0;
for (auto i:sum)
{
int temp=i.first,cnt=0;
while (temp)
{
temp/=10;
cnt++;
}
sum[cnt]+=sum[i.first];
ans+=abs(i.second);
}
cout<<ans<<"\n";
}