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