全场评价:手速场
A
题意:
做法:首先如果超过了两个数,那一定不可可以实现新放的数与所有数距离都最近。如果只有两个数,只需要把新数放在两者之间就可以,注意特判两数距离为1,此时两数之间无法放入新数。
c
o
d
e
:
code:
code:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int N = 105;
int t;
int a[N];
int n,m,h,w;
int main()
{
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
if(n==2&&(fabs(a[1]-a[2])!=1))
{
printf("YES\n");
}
else printf("NO\n");
}
return 0;
}
B
题意:
做法:
发现答案应该大致取在两个区间的交集上。如果两个区间不相交答案肯定是1。
再仔细想一想,如果交集的区间边界跟两个区间的边界都不重合,那应该是交集的区间再加上该区间前后各一扇门。如果交集的区间边界与给定两个区间的某一个边重合,那只需要加上没重合的那个边界旁边的一扇门。如果交集的区间边界与两个区间的边界都重合,那就不需要再额外扩展交集区间。
如图,蓝区间是交集区间,红黑是给定初始区间,设总答案为蓝区间长度+2,如果蓝区间的某一条边与红或者黑相交,答案就要减去1,两个边都相交,答案就减去2。
c
o
d
e
:
code:
code:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int N = 1;
int t;
int l,r,R,L;
int n,m,h,w;
int main()
{
cin>>t;
while(t--)
{
scanf("%d%d%d%d",&l,&r,&L,&R);
if(r<L||R<l)//不相交
{
printf("1\n");
continue ;
}
int lr=max(l,L);//左边界
int rr=min(r,R);//右边界
int ans=rr-lr+2;
if(l==L) ans--;//左边界重合
if(r==R) ans--;//右边界重合
printf("%d\n",ans);
}
return 0;
}
C
题意:
做法:不要被Alice&Bob唬住了,这个题和博弈论没啥关系。
发现两者每次操作一定是拿当前剩余数中最大的那个数。
具体来说,数列确定后,爱丽丝拿的一定是数列第1,3,5,,,2k-1大的数,鲍勃拿的一定是数列第2,4,6,2k大的数。最终得分就是让两个人拿的数两两做差,爱丽丝拿的第
i
i
i个数和鲍勃拿的第
i
i
i个数做差,所有的差全部加起来就是最终得分。
那么我们可以把鲍勃拿到的数逐个增大,尽可能让鲍勃拿的前
m
m
m个数与爱丽丝拿的前
m
m
m个数一样大,这样一定把贡献都给了鲍勃。
c
o
d
e
:
code:
code:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#define int long long
using namespace std;
const int N = 2e5+5;
int t;
int a[N];
int k;
int n,m,h,w;
bool cmp(int a,int b)
{
return a>b;
}
signed main()
{
cin>>t;
while(t--)
{
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+1+n,cmp);//排序
for(int i=1;i<=n;i+=2)
{
if(i>n) break;
if(i==n&&n%2) break;
if(k==0) break;
int cnt=a[i]-a[i+1];
if(k>=cnt)
{
k-=cnt;
a[i+1]=a[i];//让bob的数和Alice的一样大
}
else
{
a[i+1]+=k;//全部加到bob的数上
break;
}
}
int ans=0;
for(int i=1;i<=n;i+=2)
{
if(i>n) break;
ans+=a[i]-a[i+1];
}
for(int i=1;i<=n;i++) a[i]=0;//注意多测清空!我这里没清空挂了一次
printf("%lld\n",ans);
}
return 0;
}
D
题意:
数据范围:
n
,
q
<
=
2
∗
1
0
5
n,q<=2*10^5
n,q<=2∗105
做法:
赛时想到一种好想不好写的做法。不需要用二分或者数据结构。
首先,不难发现
x
x
x到
y
y
y的最优路径应该是
x
x
x一步传送到
y
y
y。此时
x
x
x和
y
y
y至少有一个字母是相同的。
如果
x
x
x不能一步传送到
y
y
y,则
x
x
x和
y
y
y一定形如
x
:
A
B
x:AB
x:AB和
y
:
C
D
y:CD
y:CD
那么可以从
x
x
x传到
z
1
z_1
z1,再从
z
1
z_1
z1传到
y
y
y。此时
z
1
:
A
C
o
r
A
D
o
r
B
C
o
r
B
D
z_1:AC orADorBCorBD
z1:ACorADorBCorBD;
或者从
y
y
y传送到
z
2
z_2
z2,再从
z
2
z_2
z2传送到
x
x
x。此时
z
2
:
A
C
o
r
A
D
o
r
B
C
o
r
B
D
z_2:ACorADorBCorBD
z2:ACorADorBCorBD;
那么对于任意一个
i
:
A
B
i:AB
i:AB
我们只需要统计他前面最近的
j
1
:
A
C
o
r
A
D
o
r
B
C
o
r
B
D
j_1:AC orADorBCorBD
j1:ACorADorBCorBD
以及他后面最近的
j
2
:
A
C
o
r
A
D
o
r
B
C
o
r
B
D
j_2:AC orADorBCorBD
j2:ACorADorBCorBD。
对于每个
i
i
i,分别统计这两个最短距离。我们维护每种可能的字符组合的出现位置(一共六种可能的字符)
每次询问
x
x
x和
y
y
y时,若
x
x
x能直接到
y
y
y,答案就是
∣
x
−
y
∣
|x-y|
∣x−y∣。否则答案为
m
i
n
[
d
i
s
(
x
−
>
z
1
−
>
y
)
,
d
i
s
(
y
−
>
z
2
−
>
x
)
]
min[dis(x->z_1->y),dis(y->z_2->x)]
min[dis(x−>z1−>y),dis(y−>z2−>x)]
注意一个易错的地方,那就是
z
1
z_1
z1和
z
2
z_2
z2有可能出现在
x
x
x和
y
y
y之间。不要认为
z
1
z_1
z1和
z
2
z_2
z2一定出现在
x
x
x到
y
y
y的区间之外
c
o
d
e
:
code:
code:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <unordered_map>
using namespace std;
const int N = 2e5+5;
int t;
int n,q;
string s[N];
int qian[N],hou[N];
bool check(int l,int r)
{
char a=s[l][0];char b=s[l][1];char c=s[r][0];char d=s[r][1];
if(a==c||a==d||b==c||b==d) return 1;
else return 0;
}
int main()
{
cin>>t;
while(t--)
{
scanf("%d%d",&n,&q);
int hd1=0;int hd2=0;int hd3=0;int hd4=0;int hd5=0;int hd6=0;
int la1=n+1;int la2=n+1;int la3=n+1;int la4=n+1;int la5=n+1;int la6=n+1;
for(int i=1;i<=n;i++) hou[i]=n+1,qian[i]=0;
for(int i=1;i<=n;i++)
{
cin>>s[i];
if((s[i][0]=='B'&&s[i][1]=='G')||(s[i][0]=='G'&&s[i][1]=='B'))
{
hd1=i;
qian[i]=max(max(max(hd2,hd3),hd4),hd5);
}
if((s[i][0]=='B'&&s[i][1]=='R')||(s[i][0]=='R'&&s[i][1]=='B'))
{
hd2=i;
qian[i]=max(max(max(hd1,hd3),hd4),hd6);
}
if((s[i][0]=='B'&&s[i][1]=='Y')||(s[i][0]=='Y'&&s[i][1]=='B'))
{
hd3=i;
qian[i]=max(max(max(hd1,hd2),hd6),hd5);
}
if((s[i][0]=='G'&&s[i][1]=='R')||(s[i][0]=='R'&&s[i][1]=='G'))
{
hd4=i;
qian[i]=max(max(max(hd1,hd5),hd6),hd2);
}
if((s[i][0]=='G'&&s[i][1]=='Y')||(s[i][0]=='Y'&&s[i][1]=='G'))
{
hd5=i;
qian[i]=max(max(max(hd1,hd3),hd4),hd6);
}
if((s[i][0]=='R'&&s[i][1]=='Y')||(s[i][0]=='Y'&&s[i][1]=='R'))
{
hd6=i;
qian[i]=max(max(max(hd2,hd3),hd4),hd5);
}
}
for(int i=n;i>=1;i--)
{
if((s[i][0]=='B'&&s[i][1]=='G')||(s[i][0]=='G'&&s[i][1]=='B'))
{
la1=i;
hou[i]=min(min(min(la2,la3),la4),la5);
}
if((s[i][0]=='B'&&s[i][1]=='R')||(s[i][0]=='R'&&s[i][1]=='B'))
{
la2=i;
hou[i]=min(min(min(la1,la3),la4),la6);
}
if((s[i][0]=='B'&&s[i][1]=='Y')||(s[i][0]=='Y'&&s[i][1]=='B'))
{
la3=i;
hou[i]=min(min(min(la1,la2),la6),la5);
}
if((s[i][0]=='G'&&s[i][1]=='R')||(s[i][0]=='R'&&s[i][1]=='G'))
{
la4=i;
hou[i]=min(min(min(la1,la2),la6),la5);
}
if((s[i][0]=='Y'&&s[i][1]=='G')||(s[i][0]=='G'&&s[i][1]=='Y'))
{
la5=i;
hou[i]=min(min(min(la1,la3),la4),la6);
}
if((s[i][0]=='R'&&s[i][1]=='Y')||(s[i][0]=='Y'&&s[i][1]=='R'))
{
la6=i;
hou[i]=min(min(min(la2,la3),la4),la5);
}
}
for(int i=1;i<=q;i++)
{
int l,r;
scanf("%d%d",&l,&r);
if(l>r) swap(l,r);
if(l==r)
{
printf("0\n");
continue ;
}
if(check(l,r))
{
printf("%d\n",(r-l));
continue ;
}
else
{
if(hou[l]==n+1&&qian[l]==0&&qian[r]==0&&hou[r]==n+1)
{
printf("-1\n");
continue ;
}
else if(hou[l]<=r||qian[r]>=l)
{
printf("%d\n",r-l);
}
else
{
int cnt=0x3f3f3f3f;
if(hou[l]!=n+1) cnt=min(cnt,hou[l]-l);
if(hou[r]!=n+1) cnt=min(cnt,hou[r]-r);
if(qian[l]!=0) cnt=min(cnt,l-qian[l]);
if(qian[r]!=0) cnt=min(cnt,r-qian[r]);
int ans=fabs(r-l)+cnt*2;
printf("%d\n",ans);
}
}
}
}
return 0;
}
E
题意:
做法:
发现是标准的
S
G
SG
SG问题,先打个表找一下
S
G
SG
SG函数的规律。
发现所有偶数的
S
G
SG
SG函数值为
0
0
0,第
k
k
k个质数的
S
G
SG
SG函数值就是
k
k
k。合数
x
x
x的
S
G
SG
SG函数值就是
x
x
x的最小质因数的
S
G
SG
SG函数值。
只需要用筛子筛一遍素数,顺带就把 S G SG SG函数都求出来了。
证明:我不会
c o d e : code: code:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int maxn = 3e5+5;
const int N = 1e7+5;
int t;
int a[maxn];
int sg[N];
int n,m,h,w;
bool vis[N];
int prim[N];
int head;
int tot;
int is_prime(int x)
{
for(int i=1;i<=x;i++) vis[i]=1;
sg[1]=1;
for(int i=2;i<=x;i++)
{
if(vis[i])
{
prim[++head]=i;
sg[i]=++tot;//质数的sg函数是依次数下来的
}
for(int j=1;j<=head&&i*prim[j]<=x;j++)
{
vis[i*prim[j]]=0;
if(sg[i*prim[j]]==0)
{
sg[i*prim[j]]=sg[prim[j]];//合数的sg函数是其最小质因子的sg函数
}
if(i%prim[j]==0) break;
}
}
for(int i=1;i<=x;i++) if(i%2==0) sg[i]=0;//偶数的sg函数都是0
}
int main()
{
cin>>t;
is_prime(1e7+2);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int res=sg[a[1]];
for(int i=2;i<=n;i++) res^=sg[a[i]];
if(res) printf("Alice\n");
else printf("Bob\n");
}
return 0;
}