训练赛题解
A. Puzzle From the Future (思维+水题)传送门
思路:b字符串每位只能+1或+0,而且不能使得相邻位的数据相同(因为相同会被看成一个位的数例如:1111=1,0110=00),所以从最高位开始进行+1操作,若操作后与前一位相同则+0,最后连续输出加上的1,0序列即可。
例子:
6
111000
最高位+1 → 211000 ;次高位+1 → 221000 → 21000(与前一位相同,变小了,改为+0)→ 211000;依次向后。
AC代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long ll;
const int manx = 2e5+5;
using namespace std;
char b[manx];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
scanf("%s",b);
int last = 0;
for(int i = 0;b[i] != '\0';i++){
int now = 1;
if(b[i] + now == last){
now = 0;
}
printf("%d",now);
last = now + b[i];
}
printf("\n");
}
//system("pause");
return 0;
}
B. Different Divisors(思维+埃氏筛法)[ 传送门
题目大意:输入间隔d,输出一个最小的正整数p,p满足至少有4个因数,且p的每对因数的绝对值之差大于或等于d。
思路:刚拿到题肯定会这样想:假设这个数是p,那么1和p一定是p的因子,只要再找两个p的因子满足条件即可,那直接输出
1
∗
(
1
+
d
)
∗
(
1
+
2
d
)
1*(1+d)*(1+2d)
1∗(1+d)∗(1+2d)不就好了。按照这个思路写上去会惊喜的发现,我WA了。
按照这个思路写,一定没有注意p的每个因子都要满足这个条件。例如d=2时,我们选
p
=
12
p=12
p=12,因子
1
,
3
,
6
,
12
1,3,6,12
1,3,6,12满足每对因数的绝对值之差大于或等于d。但对12的每个因子
1
,
2
,
3
,
4
,
6
,
12
1,2,3,4,6,12
1,2,3,4,6,12来说却不满足。这是因为在p的因子中存在两个因子也满足一个因子是另一个因子的除数,例如6与3。
所以我们要找的数p必须要满足p的因子不会出现存在两个因子也满足一个因子是另一个因子的除数的情况。那么我们很容易想到素数,设tar(x)为大于等于x的第一个素数,则
p
=
1
∗
t
a
r
(
1
+
d
)
∗
t
a
r
(
t
a
r
(
1
+
d
)
+
d
)
p=1*tar(1+d)*tar(tar(1+d)+d)
p=1∗tar(1+d)∗tar(tar(1+d)+d)
计算p输出即可
注:由于我们如果对每个数都判断一下它是否是素数的复杂度较高,所以我们先用筛法处理出所有素数。
AC代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long ll;
const int maxn = 5e5+5;
using namespace std;
bool isPrime[maxn];
void ShaiFa(){
int p = 0;
for(int i = 0;i <= maxn;i++){
isPrime[i] = true;
}
isPrime[0] = isPrime[1] = false;
for(int i = 2;i <= maxn;i++){
if(isPrime[i]){
for(int k = 2;k * i <= maxn;k++){
isPrime[k * i] = false;
}
}
}
}
ll tar(int x){
for(int i = x;i < maxn;i++){
if(isPrime[i]){
return ll(i);
}
}
}
int main(){
int t;
scanf("%d",&t);
ShaiFa();
while(t--){
ll d;
scanf("%lld",&d);
ll a1 = tar(1+d);
ll res = a1 * tar(a1 + d);
printf("%lld\n",res);
}
//system("pause");
return 0;
}
C. Array Destruction(思维+stl)[ 传送门
题目大意:任选一个数x,在数组a中找到两个数b与c,满足b+c=x,删除b、c,令
x
=
m
a
x
(
b
,
c
)
x=max(b,c)
x=max(b,c),再进行上述过程,知道把a中所有的数删除。
思路:由于b,c可以在数组中任意选择,但是要满足
x
=
b
+
c
x=b+c
x=b+c,那么
x
>
b
且
x
>
c
x>b且x>c
x>b且x>c,所以再删除时一定要从最大的开始删除,不然以后肯定没有机会删除大元素了。选定元素后x的变化一定是从最大的元素变化到次大的元素再变换到第三大的元素,假设b>c,所以在每次删除时,我们只需要在数组a中找到x-b即可,再令x=b依次进行上述过程即可。
但我们写代码的时候会发现,第一个整数对的较小元素怎么找????。题中
n
<
=
1000
n<=1000
n<=1000。俗话说:暴力大法好 。我们只需遍历每个元素作为第一对的较小元素,如果可以就输出答案即可。
AC代码:
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 1e4+5;
int a[maxn<<1];
int n;
void solve(){
sort(a,a+n);
multiset<int> st;
vector<pair<int,int> > ans;
for(int i = 0;i < n;i++){
st.insert(a[i]);
}
int res = 0;bool flag = false;
for(int i = 0;i < n-1;i++){
auto temp = st;
ans.clear();
auto x = *temp.rbegin();
temp.erase(temp.find(x));temp.erase(temp.find(a[i]));
ans.emplace_back(a[i],x);
while(ans.size() < n/2){
int x1 = *temp.rbegin();
temp.erase(temp.find(x1));
if(temp.find(x - x1) == temp.end()) break;
int x2 = x - x1;
ans.emplace_back(x1,x2);
temp.erase(temp.find(x2));
x = x1;
}
if(ans.size() == n / 2){
res = a[n-1] + a[i];
flag = true;
break;
}
}
if(!flag){
cout << "NO" << endl;
}else{
cout << "YES" << endl;
cout << res << endl;
for(auto&i :ans){
cout << i.first << " " << i.second << endl;
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
n*=2;
for(int i = 0;i < n;i++){
scanf("%d",a+i);
}
solve();
}
system("pause");
return 0;
}
D. Cleaning (思维+前缀后缀)传送门
题目大意:对于相邻的两个数a,b,进行操作
a
=
a
−
m
i
n
(
a
,
b
)
,
b
=
b
−
m
i
n
(
a
,
b
)
a=a-min(a,b),b=b-min(a,b)
a=a−min(a,b),b=b−min(a,b),看最后能否将所有的数都变为0。可以在处理之前任意交换相邻的两个数的位置。
思路:对序列
a
1
,
a
2
,
.
.
.
.
.
.
,
a
n
a_1,a_2,......,a_n
a1,a2,......,an,假如我们从头考虑,
a
1
a_1
a1一定与
a
2
a_2
a2一同进行运算,运算后,
a
1
=
0
a_1=0
a1=0,那
a
2
−
a
1
a_2-a_1
a2−a1即可被视为现在的
a
1
a_1
a1,再拿此数
a
2
−
a
1
a_2-a_1
a2−a1与
a
3
a_3
a3作用。如果从后向前考虑是一样的道理。
所以我们取前缀数组pre与后缀数组sub,按上述思路,令
p
r
e
[
i
]
=
a
[
i
]
−
p
r
e
[
i
−
1
]
,
s
u
b
=
a
[
i
]
−
s
u
b
[
i
+
1
]
pre[i] = a[i] - pre[i-1],sub=a[i]-sub[i+1]
pre[i]=a[i]−pre[i−1],sub=a[i]−sub[i+1]。若可能有解,则对pre必须满足
p
r
e
[
i
]
>
=
0
pre[i]>=0
pre[i]>=0且
p
r
e
[
n
]
=
0
pre[n]=0
pre[n]=0,sub必须满足
p
r
e
[
i
]
>
=
0
pre[i]>=0
pre[i]>=0且
s
u
b
[
1
]
=
0
sub[1]=0
sub[1]=0,但其中可能出现小于0的值,怎么办呢。我们把不够减的pre设为INF/2,不够减的sub设为INF。
将pre与sub处理完后,如果存在
p
r
e
[
i
]
=
s
u
b
[
i
]
pre[i]=sub[i]
pre[i]=sub[i]则可直接判断满足题意,输出YES,(这里pre设为INF/2不设为INF的目的就在于防止
p
r
e
[
i
]
=
s
u
b
[
i
]
=
I
N
F
pre[i]=sub[i]=INF
pre[i]=sub[i]=INF发生错判)。最后我们遍历每个相邻的数并进行交换,判断是否满足题意即可。
AC代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#define INF 0x7fffffff
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int maxn = 2e5+5;
int t;
int a[maxn],pre[maxn],sub[maxn];
int main(){
IOS;
cin >> t;
while(t--){
int n;
cin >> n;
fill(pre,pre+n+10,0);
fill(sub,sub+n+10,0);
for(int i = 1;i <= n;i++){
cin >> a[i];
}
//计算pre,sub
for(int i = 1;i <= n;i++){
if(a[i] >= pre[i-1])
pre[i] = a[i] - pre[i-1];
else
pre[i] = INF/2;
}
for(int i = n;i >= 1;i--){
if(a[i] >= sub[i+1])
sub[i] = a[i] - sub[i+1];
else
sub[i] = INF;
}
//遍历,尝试交换
int flag = false;
for(int i = 1;i <= n;i++){
if(pre[i] == sub[i+1]){
flag = true;
break;
}else{
//交换
int A = a[i],B = a[i+1];
if(A >= sub[i+2] && B >= pre[i-1]){
A -= sub[i+2];
B -= pre[i-1];
if(A == B){
flag = true;
break;
}
}
}
}
if(flag){
cout << "YES" << endl;
}else{
cout << "NO" << endl;
}
}
system("pause");
return 0;
}
(更新中,做出来一体更一题)