ARC 192

  • A 题中没有想好就开始写了。

  • C 题看错题+overkill。写之前再想一想。

A

先说一下我做的过程。里面会有一点问题&修正,所以最后会有整合。


首先发现如果全是 0 就不行。

否则至少有一个 1。那么 \(n=1\bmod 2\) 的情况,就一定可行。因为可以 a rc ra rc ra 这样子覆盖,只有最后一位覆盖不到,把 1 移动到哪儿就可以了。

如果 \(2\mid n\),同样如果有两个相邻的 1 也是对的。

那么现在只有 0...010...01... 这种了。不对啊,如果 \(n=0\bmod 4\) 即使是所有都是 \(0\) 都可以,可以 arcr arcr... 折返过去。

那么现在 \(n=2\bmod 4\)。观察每一段 0 的个数。如果是 000001 这种,只有 arcrarc 这种覆盖,就必须接下去;如果是 00001 这种,arcra 就好了(因为偶数个 0 相当于以前的 \(n=1\bmod 2\)!)所以可以“重启”。如果有一次能重启就是好的,所以如果有长度为偶数的段就可以了。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 4e5+5;

int n,a[N];

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	int s=0;
	for (int i=1; i<=n; i++){
		cin>>a[i];
		s+=a[i];
		a[i+n]=a[i];
	}
	if (s==0){
		if (n%4==0) cout<<"Yes\n"; 
		else cout<<"No\n";
		return 0;
	}
	if (n%2==1){
		cout<<"Yes\n";
		return 0;
	}
	if (a[1] && a[n]){
		cout<<"Yes\n";
		return 0;
	}
	for (int i=1; i<n; i++){
		if (a[i] && a[i+1]){
			cout<<"Yes\n";
			return 0;
		}
	}
	if (n%4==0){
		cout<<"Yes\n";
	}
	else{
		int xr=0,p=1;
		while (a[p]==0) p++;
		for (int i=p; i<=p+n-1;){
			int j=i;
			while (a[j]==0){
				j++;
			}
			j--;
			if ((j-i+1)%2==0 && i<=j){
				xr++;
			}
			i=j+2;
		}
		if (!xr) cout<<"No\n";
		else cout<<"Yes\n";
	}
	return 0;
}

能不能简洁一点?

  • 首先如果 \(n=0\bmod 4\),答案是 Yes

  • 如果 \(2\nmid n\):如果不是全 0 则是 Yes;否则是 No

  • 前面讨论的“相邻的 1”,“有 0 长度为偶数的段”可以总结为 1 的位置有奇数位也有偶数位。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 2e5+5;

int n,a[N];

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	int s=0;
	for (int i=1; i<=n; i++){
		cin>>a[i];
		s+=a[i];
	}
	if (n%4==0){
		cout<<"Yes\n";
	}
	else if (n%2){
		if (s) cout<<"Yes\n";
		else cout<<"No\n";
	}
	else{
		int c0=0,c1=0;
		for (int i=1; i<=n; i++){
			if (a[i]){
				if (i&1) c1++;
				else c0++;
			}
		}
		if (c0 && c1) cout<<"Yes\n";
		else cout<<"No\n";
	}
	return 0;
}

B

如果 \(n=1\) 一定 F 赢。

如果 \(n=2\) 一定 S 赢。

统计 \(a_i\) 中偶数奇数个数,记为 \(c_0,c_1\)。现在 F 希望能前 \(n-2\) 个加入的数的和是奇数,这样就赢了。因为还有两个是要留下的,所以可以先讨论一些比较小的情况。

  • 如果 \(n=3\)\(c_1\neq 0\) 则 F 赢,否则 S 赢。(F 先取一个奇)

  • 如果 \(c_0=0\)\(c_1\) 奇则 F 赢,否则 S 赢。

  • 如果 \(c_1=0\),S 赢。

  • 如果 \(c_0=1\),如果 \(2\mid c_1\),S 赢,否则 F 赢。

  • 否则,如果 \(2\mid c_1\),S 赢;反之亦然。(S 可以对称得取,F 取奇数我就取奇数,F 取偶数我就取偶数)

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 2e5+5;

int n,a[N];

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	for (int i=1; i<=n; i++) cin>>a[i];
	if (n==1){
		cout<<"Fennec\n";
		return 0;
	}
	if (n==2){
		cout<<"Snuke\n";
		return 0;
	}
	int c0=0,c1=0;
	for (int i=1; i<=n; i++){
		if (a[i]%2==0) c0++;
		else c1++;
	}
	if (n==3){
		if (c1) cout<<"Fennec\n";
		else cout<<"Snuke\n";
		return 0;
	}
	if (c0==0){
		if (c1&1) cout<<"Fennec\n";
		else cout<<"Snuke\n";
		return 0;
	}
	if (c1==0){
		cout<<"Snuke\n";
		return 0;
	}
	if (c0==1){
		if (c1%2==0) cout<<"Snuke\n";
		else cout<<"Fennec\n";
		return 0;
	}
	if (c1&1) cout<<"Fennec\n";
	else cout<<"Snuke\n";
	return 0;
}

C

先讲正常简单做法,再讲 overkill。


首先询问所有点和 \(1\),记为 \(l_1(i)\),则 \(l_1(i)\) 最大的那个 \(d\) 一定是端点。不妨假设是左端点。

询问所有点和 \(d\),记为 \(l_d(i)\)。按照 \(l_d\) 排序,则我们可以算出除了 \(a_1\) 以外的 \(p_1\sim p_n,a_2\sim a_n\)\(a_1=l_d(3)-ask(p^{-1}(2),p^{-1}(3))\)。因此可以用 \((n-1)+(n-1)+1=2n-1\) 次询问求出答案。

如何判断 \(d\) 是哪个端点?因为 \(p_1<p_2\) 所以可以得知。这个条件本身就是用来“定向”的,否则所有结果都可以是两个答案。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 5e3+3;

ll n,p[N],a[N],l1[N],ld[N],id[N];

ll ask(int x,int y){
	cout<<"? "<<x<<" "<<y<<endl;
	ll res;
	cin>>res;
	return res;
}

bool cmp(int x,int y){
	return ld[x]<ld[y];
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	ll mx=0;
	for (int i=2; i<=n; i++){
		l1[i]=ask(1,i);
		mx=max(mx,l1[i]);
		p[i]=i;
	}
	p[1]=1;
	int d=0;
	for (int i=2; i<=n; i++){
		if (l1[i]==mx) d=i;
	}
	for (int i=1; i<=n; i++){
		if (i!=d) ld[i]=ask(d,i);
	}
	sort(p+1,p+1+n,cmp);
	int fl=ld[1]>ld[2];
	for (int i=1; i<=n; i++){
		id[p[i]]=i;
	}
	sort(ld+1,ld+1+n);
	a[1]=ld[1]=ld[3]-ask(p[2],p[3]);
	for (int i=2; i<=n; i++) a[i]=ld[i]-ld[i-1];
	if (fl){
		for (int i=1; i<=n; i++){
			id[i]=n-id[i]+1;
		}
		reverse(a+1,a+1+n);
	}
	cout<<"! ";
	for (int i=1; i<=n; i++) cout<<id[i]<<" ";
	for (int i=1; i<=n; i++) cout<<a[i]<<" ";
	return 0;
}

说来搞笑,但是这个做法用了 \(2n-2\) 次询问,但是多很多的码量。

首先考虑如果 \(i=j\) 是可以的。那么我们令 \(l_1(i)=ask(1,i),l_2(i)=ask(2,i)\)。我们发现 \(l_1(i)-l_2(i)\) 最小的一定都 \(\le p_1\)\(l_1(i)-l_2(i)\) 最大的一定都是 \(\ge p_2\),而其他的在中间。三块(\(l,v,r\))我们按照 \(l_1,l_2\) 中的一个排序就可以算出 \(p,a\) 了。

但是现在我们不能求 \(l_1(1),l_2(2)\)。这个会导致 \(1,2\) 是端点的时候出错。这个时候看看出错的可能性:\(l\) 中的其实是 \(v\) 中;\(r\) 中的其实是 \(v\) 中;两个是 \(v\) 中的;甚至 \(l\)\(r\) 中的,\(r\)\(l\) 中的。后两种如果我们 \(mn\ge 0\) 不加入 \(l\) 并且 \(mx\le 0\) 不加入 \(r\) 可以避免。因此要讨论这些情况并且讨论合法性。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 5e3+3;

ll n,p[N];
ll l1[N],l2[N];

ll ask(ll x,ll y){
	if (x==y){
		return -1;
	}
	cout<<"? "<<x<<" "<<y<<endl;
	ll res;
	cin>>res;
	return res;
}

bool cmp(int a,int b){
	return l1[a]<l1[b];
}

bool cmpl(int a,int b){
	return l1[a]>l1[b];
}

ll ans[N],id[N],s[N];

bool cp(int x,int y){
	return id[x]<id[y];
}

void sol(vector<int> v,vector<int> l,vector<int> r){
	memset(ans,0,sizeof ans);
	memset(id,0,sizeof id);
	sort(v.begin(),v.end(),cmp);
	sort(l.begin(),l.end(),cmpl);
	sort(r.begin(),r.end(),cmp);
	if (v.size()){
		int x=v[0];
		l1[1]=l2[1]-l2[x];
		x=v.back();
		l2[2]=l2[1]-l1[x];
	}
	else if (l.size()){
		int x=l[0];
		l2[2]=l2[x]-l1[x];
		l1[1]=l2[1]-l2[2];
	}
	else{
		int x=r[0];
		l1[1]=l1[x]-l2[x];
		l2[2]=l1[2]-l1[1];
	}
	l.push_back(1);
	r.push_back(2);
	sort(l.begin(),l.end(),cmpl);
	sort(r.begin(),r.end(),cmp);
	int cnt=0;
	for (auto u : l) id[u]=++cnt;
	for (auto u : v) id[u]=++cnt;
	for (auto u : r) id[u]=++cnt;
	vector<ll> w;
	for (int i=1; i<=n; i++){
		if (id[i]>=id[1]) w.push_back(i);
	}
	sort(w.begin(),w.end(),cp);
	int ls=0;
	for (auto u : w){
		ans[id[u]]=l1[u]-l1[ls];
		ls=u;
	}
	w.clear();
	for (int i=1; i<=n; i++){
		if (id[i]<id[1]) w.push_back(i);
	}
	sort(w.begin(),w.end(),cp);
	reverse(w.begin(),w.end());
	ls=1;
	for (auto u : w){
		ans[id[u]]=l2[u]-l2[ls];
		ls=u;
	}
	for (int i=1; i<=n; i++){
		if (ans[i]<=0) return;
		s[i]=s[i-1]+ans[i];
	}
	for (int i=1; i<=n; i++){
		if (l1[i]!=s[max(id[1],id[i])]-s[min(id[1],id[i])-1]) return;
		if (l2[i]!=s[max(id[2],id[i])]-s[min(id[2],id[i])-1]) return;
	}
	int X=0,Y=0;
	for (int i=1; i<=n; i++){
		if (id[i]==1) X=i;
		if (id[i]==n) Y=i;
	}
	if (l1[X]+l1[Y]-l1[1]!=s[n]) return;
	cout<<"! ";
	for (int i=1; i<=n; i++){
		cout<<id[i]<<" ";
	}
	for (int i=1; i<=n; i++) cout<<ans[i]<<" ";
	cout<<endl;
	exit(0);
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	ll mn=1e18,mx=-1e18;
	for (int i=1; i<=n; i++){
		l1[i]=ask(1,i);
		l2[i]=ask(2,i);
		if (i>2) mn=min(mn,l1[i]-l2[i]);
		if (i>2) mx=max(mx,l1[i]-l2[i]);
	}
	vector<int> v,l,r;
	for (int i=3; i<=n; i++){
		if (l1[i]-l2[i]==mn && mn<0){
			l.push_back(i);
		}
		else if (l1[i]-l2[i]==mx && mx>0){
			r.push_back(i);
		}
		else{
			v.push_back(i);
		}
	}
	sol(v,l,r);
	vector<int> l_={},v_=v,r_={};
	for (auto u : l) v_.push_back(u);
	sol(v_,l_,r);
	v_=v;
	for (auto u : r) v_.push_back(u);
	sol(v_,l,r_);
	for (auto u : l) v_.push_back(u);
	sol(v_,l_,r_);
	return 0;
}

D

首先 \(f(\frac{S_i}{S_{i+1}})=\frac{S_iS_{i+1}}{\gcd(S_i,S_{i+1})^2}=A_i\)

\(S_i=\prod_k p_k^{a_{i,k}}\)。设 \(A_i=\prod_k p_k^{b_{i,k}}\)

对于 \(p_k\) 的贡献:\(\frac{p_k^{a_{i,k}+a_{i+1,k}}}{p_k^{2\min(a_{i,k},a_{i+1,k})}}=p_k^{|a_{i,k}-a_{i+1,k}|}\)

那么得到 \(|a_{i,k}-a_{i+1,k}|=b_{i,k}\)

\(\gcd(S_1\sim S_n)=1\) 可以推出 \(\forall k,\min(a_{i,k})=0\)

因为 \(\sum \sum \prod \prod=(\sum \prod)(\sum \prod)\),一次类推,所以要记录 \(\sum_{a_{1\sim n,k}}\prod_{i=1}^n p_k^{a_{i,k}}\)

\(dp_{i,j,0/1}\)\(a_{i,k}=j\),有无 \(0\),的 \(\sum \prod\)

\(\mathcal{O}(\sum_k(n\sum_i b_{i,k}))=\mathcal{O}(n^2\log V)\)

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 1e3+3;
const ll mod = 998244353;

int n,b[N][N],pr[N];
ll dp[N][N*11][2],pw[N*11],ans=1;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	pr[0]=pr[1]=1;
	for (int i=2; i<N; i++){
		if (pr[i]) continue;
		for (int j=i+i; j<N; j+=i){
			pr[j]=1;
		}
	}
	cin>>n;
	for (int i=1; i<n; i++){
		int x;
		cin>>x;
		for (int j=2; j<N; j++){
			while (x%j==0) b[i][j]++,x/=j;
		}
	}
	for (int p=2; p<N; p++){
		if (pr[p]) continue;
		int m=0;
		for (int i=1; i<n; i++) m+=b[i][p];
		pw[0]=1;
		for (int i=1; i<=m; i++) pw[i]=pw[i-1]*p%mod;
		for (int i=1; i<=n; i++){
			for (int j=0; j<=m; j++){
				for (int f=0; f<2; f++) dp[i][j][f]=0;
			}
		}
		for (int j=0; j<=m; j++) dp[1][j][0]=1;
		for (int i=1; i<=n; i++){
			(dp[i][0][1]+=dp[i][0][0])%=mod;
			dp[i][0][0]=0;
			for (int j=0; j<=m; j++){
				for (int f=0; f<2; f++){
					(dp[i][j][f]*=pw[j])%=mod;
					if (i==n) continue;
					int t;
					if ((t=j+b[i][p])<=m){
						(dp[i+1][t][f]+=dp[i][j][f])%=mod;
					}
					if ((t=j-b[i][p])>=0 && b[i][p]){
						(dp[i+1][t][f]+=dp[i][j][f])%=mod;
					}
				}
			}
		}
		ll tmp=0;
		for (int j=0; j<=m; j++) (tmp+=dp[n][j][1])%=mod;
		ans=ans*tmp%mod;
	}
	cout<<ans<<"\n";
	return 0;
}
posted @ 2025-02-10 20:14  SFlyer  阅读(18)  评论(0)    收藏  举报