AtCoder Regular Contest 155
期末考完复健,补一下一个月前打的ARC
当时赛后9秒过D,太痛了,第一次体验这种
只能说,幸好当时要打的时候感觉状态不行,就unrated了
比赛的状况是:A不知道哪错了;C不会;D博弈DP原本状态假了,想到用容斥改对,但总觉得有些奇怪不太敢写,最后才莽了一下,发现真是对的,但写的比较暴力,赛后9秒才改对。。。
A
B
发现所求式子可以转化成:区间内到两个点的距离的较小值的最小值;而对于多个点,这又等价于到所有点距离最小值的最小值;那么是个经典问题,multiset维护即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
multiset<int>st;
int q,t,a,b;
int main()
{
//srand(time(0));
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
//int T; cin>>T; while(T--) work();
cin>>q>>a>>b;
st.insert(a+b); st.insert(a-b);
while(q--){
scanf("%d%d%d",&t,&a,&b);
if(t==1){
st.insert(a+b); st.insert(a-b);
}
else{
int ans=1e9;
multiset<int>:: iterator it=st.lower_bound(a);
if(it!=st.end()) ans=max(*it-b,0);
it=st.upper_bound(b);
if(it!=st.begin()){
it--;
ans=min(ans,max(a-*it,0));
}
printf("%d\n",ans);
}
}
return 0;
}
C
感觉是目前遇到的最难的ARC的C题
赛后想了下,得到的结论是:
记奇数为1,偶数为0
情况1:A只要有连续三个数恰好有两个1
,整个序列就可以任意重排;
情况2:否则只能把每段连续的长度大于2的0的重排。
实际上前者很假,因为两个相邻/只隔一个0
的1没法换到隔多个0
,即情况1没法变成情况2。从宏观一点的角度考虑,交换的操作是可逆的,上述情况2没法变成情况1,反之亦然。那么只有A和B是同种情况时可能是Yes,而都是情况2是好判的。
对于都是情况1,考虑能交换成什么:1之间可以通过一个0实现相对顺序任意排列;1和0之间的相对顺序也可以任意排列;对于0之间,通过上述操作可以换成连续一段,那么就是个数大于2即可任意排列。
于是就判断A和B的元素是否相同,以及如果恰有两个0,它们的相对顺序要相同。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int tmp[N];
void make(int* a,int n){
bool pd=0;
for(int i=2;i<=n;i++) if(a[i]&1 && ( a[i-1]&1 || (i>2 && a[i-2]&1) )) pd=1;
if(!pd) return;
int s1=0,s2=0;
for(int i=1;i<=n;i++){
if(a[i]&1) tmp[++s1]=a[i];
else a[++s2]=a[i];
}
for(int i=1;i<=s1;i++) a[s2+i]=tmp[i];
if(s2>2) sort(a+1,a+s2+1);
sort(a+s2+1,a+n+1);
}
int n,a[N],b[N];
bool chk(int l,int r){
if(r-l+1>2){
sort(a+l,a+r+1);
sort(b+l,b+r+1);
}
for(int i=l;i<=r;i++) if(a[i]!=b[i]) return 0;
return 1;
}
int main()
{
cin>>n;
int ct=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(!(a[i]&1)) ct++;
}
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
if(!ct){
for(int i=1;i<=n;i++) if(a[i]!=b[i]){
puts("No");
return 0;
}
puts("Yes");
return 0;
}
for(int i=2;i<=n;i++) if(a[i]&1 && ( a[i-1]&1 || (i>2 && a[i-2]&1) )){
make(a,n);
make(b,n);
for(int j=1;j<=n;j++) if(a[j]!=b[j]){
puts("No");
return 0;
}
puts("Yes");
return 0;
}
int lt=1;
for(int i=1;i<=n;i++) if(a[i]&1){
if(lt<i){
//cout<<lt<<" "<<i-1<<endl;
if(!chk(lt,i-1)){
puts("No");
return 0;
}
}
if(a[i]!=b[i]){
puts("No");
return 0;
}
lt=i+1;
}
if(lt<=n && !chk(lt,n)){
puts("No");
return 0;
}
puts("Yes");
return 0;
}
D
首先可以得到一个DP:f[g][i]表示目前的数是g,已经拿了i个数,是否有必胜策略。由于已经拿的数都是g的倍数,只需要考虑下一个拿的数是否为g的倍数:如果是直接i+1;否则枚举新的gcd,用n个数中是g的倍数的个数作为状态上界即可。
但在枚举新的gcd的时候,必须保证,而如果仅判断上届只能保证(原本想把状态改成g|目前的数
,但对于博弈DP来说,状态是不能相互交叉的,在最优化问题才可以)但把后者转成前者是很套路的,莫比乌斯反演即可计算出正好满足前者的个数。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
vector<int>d[N],f[N];
int u[N],pri[N];
bool vis[N];
void init(){
int n=200000,tot=0;
u[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
pri[++tot]=i;
u[i]=-1;
}
for(int j=1;j<=tot && pri[j]*i<=n;j++){
int x=pri[j]*i;
vis[x]=1;
if(!(i%pri[j])){
u[x]=0;
break;
}
u[x]=-u[i];
}
}
for(int i=2;i<=n;i++){
for(int j=i;j<=n;j+=i) d[j].push_back(i);
}
}
int n,a[N],mul[N],is[N];
int main()
{
init();
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
int z=d[a[i]].size();
for(int j=0;j<z;j++){
int x=d[a[i]][j];
mul[x]++;
}
}
for(int g=2;g<=200000;g++) if(mul[g]){
f[g].resize(mul[g]+1);
int z=d[g].size();
for(int j=0;j<z-1;j++) is[j]=0;
for(int j=0;j<z-1;j++){
int x=d[g][j];
int s=0;
for(int k=0;k<z;k++){
int y=d[g][k];
if(!(y%x)) s+=u[y/x]*mul[y];
}
if(s) is[j]=1;
}
for(int i=mul[g];i;i--){
f[g][i]=0;
for(int j=0;j<z-1;j++){
int x=d[g][j];
if(is[j] && i+1<=mul[x] && !f[x][i+1]) f[g][i]=1;
}
if(i<mul[g] && !f[g][i+1]) f[g][i]=1;
}
}
for(int i=1;i<=n;i++){
bool pd=f[a[i]][1];
if(pd) puts("Aoki");
else puts("Takahashi");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通