Codeforces Global Round 17
A. Anti Light's Cell Guessing
坑點:\(n=1,m=1\) 時答案為 \(0\) 。
其他情況:當 \(n=1\) 或 \(m=1\) 時,只需要取端點即可。其他情況只需要兩個點,也是取兩個端點,把離一個點曼哈頓距離為固定值的點連成一條線段,可以發現這兩個端點形成的線段只可能有一個交點,即隱藏點。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std;
using ll = long long;
constexpr int N = 2e5+5, P = 1e9+7;
int main() {
cin.tie(nullptr)->ios::sync_with_stdio(false);
int _; cin>>_;
while (_--) {
ll n,m; cin>>n>>m;
if(n==1&&m==1)cout<<0<<endl;
else if(n==1||m==1)cout<<1<<endl;
else {
cout<<2<<endl;
}
}
return 0;
}
B. Kalindrome Array
和之前cf出過的一題一模一樣,那題是刪字母,這題是刪數字,不過都一樣,因為可能刪的值只可能有兩個,枚舉這兩個可能值即可。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std;
using ll = long long;
constexpr int N = 2e5+5, P = 1e9+7;
int a[N];
int main() {
cin.tie(nullptr)->ios::sync_with_stdio(false);
int _; cin>>_;
while (_--) {
int n; cin>>n;
rep(i,0,n)cin>>a[i];
int lx=-1,ly=-1;
int l=0,r=n-1;
while(l<r){
if(a[l]!=a[r]){
if(lx==-1){
lx=a[l]; ly=a[r];
break;
}
}else{
l++,r--;
}
}
l=0,r=n-1;
int flag=1;
while(l<r){
if(a[l]!=a[r]){
if(a[l]==lx){
while(l<r&&a[l]==lx)l++;
}else if(a[r]==lx){
while(l<r&&a[r]==lx)r--;
}else{
flag=0;
break;
}
if(a[l]!=a[r]){
flag=0;
break;
}
}else{
l++,r--;
}
}
if(flag){
cout<<"yes\n";
continue;
}
l=0,r=n-1;flag=1;
while(l<r){
if(a[l]!=a[r]){
if(a[l]==ly){
while(l<r&&a[l]==ly)l++;
}else if(a[r]==ly){
while(l<r&&a[r]==ly)r--;
}else{
flag=0;
break;
}
if(a[l]!=a[r]){
flag=0;
break;
}
}else{
l++,r--;
}
}
if(!flag){
cout<<"no\n";
}else{
cout<<"yes\n";
}
}
return 0;
}
C. Keshi Is Throwing a Party
由於答案具有單調性,考慮二分答案。於是問題就轉化成了從 \(n\) 個人里選 \(k\) 個人,使他們都開心。
假設選了 \(k\) 個人的下標分別為 \(p_1,p_2,\cdots,p_k\) 。對於 \(i\in[1,k]\) ,都有
根據這個性質,我們就可以貪心地取了。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std;
using ll = long long;
constexpr int N = 2e5+5, P = 1e9+7;
int r[N], p[N], n;
int check(int x) {
int ans=0,cc=0;
rep(i,0,n){
if(cc+1>=x-r[i]&&cc+1<=p[i]+1)cc++;
}
return cc>=x;
}
int main() {
cin.tie(nullptr)->ios::sync_with_stdio(false);
int _; cin>>_;
while (_--) {
cin>>n;
rep(i,0,n)cin>>r[i]>>p[i];
int l=1,r=n;
while(l<r){
int mid=l+r+1>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
return 0;
}
D. Not Quite Lee
一個數 \(x\) 形成的連續數字的和可以表示為
於是一個數列 \((x_1,x_2,\cdots,x_k)\) 的合法性可以表示為
移項得
為表示簡略,以下的冪次都是指 \(2\) 的冪次。
當數列中有一個為奇數時,右邊的最低冪次為 \(0\) ,最低冪次為 \(0\) 的數可以表示任何數,也就是說,當數列中有一個為奇數,該數列即合法。於是考慮數列中全為偶數的情況,等式右邊的最低冪次一定比左邊的最低冪次大 \(1\) 。因為 \(x\) 是偶數,\(x+1\) 是奇數,乘起來再除個 \(2\) 必定會丟失一個因子 \(2\) 。低冪次的可以表示高冪次的,高冪次的不能表示低冪次,而左邊的最低冪次要比右邊小,所以要保證數列中冪次為最低冪次的數有偶數個,才能使他們的最低冪次 \(+1\) ,從而被右邊的表示出來。於是思路就顯而易見了,枚舉最低冪次,若這個冪次不為 \(0\) ,那只能挑偶數個,否則都可以選。
從 \(n\) 個數中挑偶數個數(不能不挑)的方案數為
其實根據二項式定理,二項式系數中的偶數項和奇數項的和其實是一樣的,也就是說,上述的式子其實就是
然后就可以寫了。一個小技巧,求一個數的冪次可以用內建函數 __builtin_ctz(x)
求得。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std;
using ll = long long;
constexpr int N = 2e5+5, P = 1e9+7;
ll qpow(ll a, ll b) {
ll res=1;
for(;b;b>>=1,a=(a*a)%P)if(b&1)res=res*a%P;
return res;
}
int cc[35];
int main() {
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);
#endif // !ONLINE_JUDGE
cin.tie(nullptr)->ios::sync_with_stdio(false);
int n; cin>>n;
rep(i,0,n){
int x; cin>>x;
cc[__builtin_ctz(x)] ++;
}
ll ans=0, tot=0;
for(int i=31;i>=0;i--){
if(cc[i]){
if(i) ans=(ans+qpow(2,tot)*(qpow(2,cc[i]-1)-1)%P)%P;
else ans=(ans+qpow(2,tot)*(qpow(2,cc[i])-1)%P)%P;
tot += cc[i];
}
}
cout<<ans;
return 0;
}