AtCoder Regular Contest 155

期末考完复健,补一下一个月前打的ARC
image
image
当时赛后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=gcd(g,a[k]),而如果仅判断上届只能保证g|gcd(g,a[k])(原本想把状态改成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;
}

posted @   sz[sz]  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示