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;
}