A. Tit for Tat
从前往后枚举,把前面的数减到0,然后给最后一个数加上去。操作次数没了就停止
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
long long sum=1;
bool vis[N];
int a[N];
int main()
{
int t;
cin>>t;
while(t--)
{
int n,k,g=0;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
int now=a[i];
a[i]=max(0,a[i]-k);
k-=now-a[i];
g+=now-a[i];
}
for(int i=1;i<n;i++)printf("%d ",a[i]);
cout<<a[n]+g;
cout<<endl;
}
return 0;
}
B. AGAGA XOOORRR
用前缀异或。
比如异或出来的前缀数组是
a数组 1 4 5 7 0 4 3 4 0 9 8
下标:1 2 3 4 5 6 7 8 9 10 11
可以发现在0前面的任何一个数x,都可以让0前面变成两段。两段各自的异或就可以是x。比如1-3区间异或为a[3]=5。4-5的异或为a[5]^a[3]=5。则这两段异或值相同
记录最后一个0的位置,如果0在最后。则区间划分成两段。
否则划分成3段。记录0前面有没有数等于0后面的异或值即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
long long sum=1;
int a[N];
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
bool f=0;
int q=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]^=a[i-1];
if(a[i]==0)q=i;
}
int sum=a[n];
for(int i=1;i<q;i++)if(a[i]==sum)f=1;
if(f||q==n)puts("YES");
else puts("NO");
}
return 0;
}
C. Baby Ehab Partitions Again
用
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前i个物品取体积不超过j的最大价值。这里把价值当作体积。
某个子序列的和是x 则
f
[
n
]
[
x
]
=
=
x
f[n][x]==x
f[n][x]==x,否则说明取不到和是x的子序列
如果有两个子序列和一样,那么这个和加起来一定是偶数。
如果是奇数,直接输出0
否则 令当前所有物品体积和为s。看是否有和是s/2,如果没有则输出0。否则一定会删除1个数。遍历一遍每个数,如果是奇数直接输出。因为偶数-奇数=奇数,一定不会有两个一样的和加起来是偶数。或者不存在(s-a[i])/2的和也直接输出
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int f[N][N*2000+10],a[N];
int main()
{
int n;
cin>>n;
int m=n*2000,s=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s+=a[i];
for(int j=a[i];j<=m;j++)
{
f[i][j]=max(f[i-1][j],f[i-1][j-a[i]]+a[i]);
}
}
if(s&1)cout<<0;
else
{
if(f[n][s/2]!=s/2)
{
cout<<0;
return 0;
}
cout<<1<<endl;
for(int i=1;i<=n;i++)
{
if(a[i]&1||(f[n][(s-a[i])/2]!=(s-a[i])/2))
{
cout<<i;
return 0;
}
}
}
return 0;
}
D Cut
首先要使分割的每一段里面的数两两都是互质的。
比如有两个数质因数分解分别为
a
=
2
3
∗
3
4
∗
5
2
a=2^3*3^4*5^2
a=23∗34∗52 和
b
=
2
1
∗
3
5
b=2^1*3^5
b=21∗35
则他们的乘积就是
2
4
∗
3
9
∗
5
2
2^4*3^9*5^2
24∗39∗52 lcm为
2
3
∗
3
4
∗
5
2
2^3*3^4*5^2
23∗34∗52
要使乘积等于lcm,则每个质因子只能a有或者只能b有。
1.考虑
n
e
[
i
]
ne[i]
ne[i]:位置i左边最近的非互质的数的位置
a: 2 3 10 7 5 14
ne :0 0 1 0 3 4
有了ne[i]我们已经可以计算答案了,假如i位置跳到ne[i],我们自然会想把ne[i]+1~i划分为一组。但如果这里面还有两个数是非互质的呢
比如 14 10 5 7 ne[4]=1 ne[3]=2显然我们只能把5和7这两个数组划分为一组。
所以ne[i]=max(ne[i],ne[i-1])即可
2.计算 ne[i] ,从前往后对每个数质因数分解,当前枚举到位置 i ,对a[i]所有的质因子j 。
n
e
[
i
]
=
m
a
x
(
n
e
[
i
]
,
w
e
i
[
j
]
)
ne[i]=max(ne[i],wei[j])
ne[i]=max(ne[i],wei[j])。。。
w
e
i
[
j
]
wei[j]
wei[j]表示j这个数的位置。然后更新记录这质因子的位置为
w
e
i
[
j
]
=
i
;
wei[j]=i;
wei[j]=i;
3.用倍增法快速跳跃。一步步跳可能会超时。倍增法枚举log次即可。
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示从 j 这个位置跳
2
i
2^i
2i步到的位置。
则
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
f
[
i
−
1
]
[
j
]
]
;
f[i][j]=f[i-1][f[i-1][j]];
f[i][j]=f[i−1][f[i−1][j]];
即先跳一半再跳一半嘛。
4.计算答案。i从20开始枚举因为
2
20
2^{20}
220>
1
e
5
1e^5
1e5,即从跳
2
i
2^i
2i步开始枚举,从r往l跳,只要跳到范围没有超过l(<l)就跳过去。然后答案加上
2
i
2^i
2i,最终答案还要加上1,要多跳一下。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int ne[N],a[N],wei[N],f[20][N];//wei表示i这个数的位置
int main()
{
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++)
{
int x=a[i];
for(int j=2;j*j<=x;j++)
{
if(x%j==0)
{
ne[i]=max(ne[i],wei[j]);
wei[j]=i;
while(x%j==0)
{
x/=j;
}
}
}
if(x>1)
{
ne[i]=max(ne[i],wei[x]);
wei[x]=i;
}
ne[i]=max(ne[i],ne[i-1]);
f[0][i]=ne[i];
}
for(int i=1;i<=19;i++)
{
for(int j=1;j<=n;j++)f[i][j]=f[i-1][f[i-1][j]];
}
while(q--)
{
int l,r,x,ans=0;
cin>>l>>r;
x=r;
for(int i=19;i>=0;i--)
{
if(f[i][x]>=l)
{
ans+=1<<i;
x=f[i][x];
}
}
printf("%d\n",ans+1);
}
return 0;
}
/*
5 3
2 14 10 5 7
1 5
*/
有不懂的欢迎评论出来一起探讨!