CF 杂记
CF Round 1116 (Div. 2)
2026.8.10 在海亮VP的,过了C以后就倒闭了。
A. Three Numbers on the Blackboard
link.
注意到操作超过 1 次不会让答案变得更优。
对\(a\),\(b\),\(c\)排序,那么替换\(b\)一定不优。
如果替换\(a\)或\(c\),答案为\((b+c) - c\)或\((a+b) - a\),即\(b\);
如果不替换,答案为\(c - a\)。
对两种情况取 min 即可。
Code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define inf 1e18
#define eps 1e-9
#define ls 2*k
#define rs 2*k+1
using namespace std;
const int N=2e5+5,M=4e5+5;
const int mod=1e9+7;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int n=3,a[4];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int tc;cin>>tc;
while(tc--){
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
cout<<min(a[n]-a[1],a[2])<<"\n";
}
return 0;
}
B. Domino Tiles
link.
题目中的限制可以转化为任意\(i\),均满足\(s_i \neq s_{i-2}\)。
那么奇偶性不同的位置之间是没有影响的,可以分别考虑。
如果一个位置确定了,那么与这一位置奇偶性相同的位置都确定了。
判一下无解,然后如果全是 ?,就答案乘 2。
Code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define inf 1e18
#define eps 1e-9
#define ls 2*k
#define rs 2*k+1
using namespace std;
const int N=2e5+5,M=4e5+5;
const int mod=998244353;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int n;
string s;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int tc;cin>>tc;
while(tc--){
cin>>n>>s;
s=" "+s;
int cnt=0,ans=1;
for(int i=1;i<=n;i+=2)
if(s[i]!='?') cnt++;
if(!cnt) ans*=2;
cnt=0;
for(int i=2;i<=n;i+=2)
if(s[i]!='?') cnt++;
if(!cnt) ans*=2;
int now=2;
for(int i=1;i<=n;i+=2){
if(s[i]!='?' && now>1) now=s[i]-'0';
if(now<=1 && s[i]!='?' && now!=(s[i]-'0')) ans=0;
if(now<=1) now^=1;
}
now=2;
for(int i=2;i<=n;i+=2){
if(s[i]!='?' && now>1) now=s[i]-'0';
if(now<=1 && s[i]!='?' && now!=(s[i]-'0')) ans=0;
if(now<=1) now^=1;
}
cout<<ans<<"\n";
}
return 0;
}
C. Hot Potatoes at the Fairy Warehouse
link.
土豆一定会在最后一轮传递(如果先传递了,那么对手可以在最后一轮传回来,这一定不优)。
那么最后一轮,一定是能传下去就传下去。
Code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define inf 1e18
#define eps 1e-9
#define ls 2*k
#define rs 2*k+1
using namespace std;
const int N=2e5+5,M=4e5+5;
const int mod=998244353;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
bool vis[N];
int n,k,tot;
string s;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int tc;cin>>tc;
while(tc--){
cin>>n>>k>>s; s=" "+s; tot=0;
for(int i=1;i<=2*n;i++)
if(s[i]=='1') ++tot;
for(int i=1;i<=2*n;i++){
int to=i%(2*n)+1;
if(s[i]=='1' && s[to]=='0') vis[to]=1;//cout<<to<<'\n';
}
for(int i=1;i<=2*n;i++){
if(vis[i]==1){
int from=(i-1 ? i-1 : 2*n);
s[from]='0';
}
}
int ans=0;
for(int i=1;i<=2*n;i+=2)
if(s[i]=='1' || vis[i]==1) ans++;
cout<<tot-ans<<" "<<ans<<"\n";
for(int i=1;i<=2*n;i++) vis[i]=0;
}
return 0;
}
D. A Ribbon for Tomorrow
link.
将原序列写成01子串长度的形式。
区间翻转操作,记端点为\(l , r(s_l = s_r)\)。
如果\(s_{l+1} = s_{r-1}\),那不妨令\(l = l + 1\),\(r = r - 1\)。
只考虑\(s_{l+1} \neq s_{r-1}\)的情况。
考虑它们所属的两个连续段,至于中间部分,自然可以再做一次翻转操作复原回去。
这个操作相当于改变了两个颜色相同的连续段的长度。
推广一下,可以发现,不同颜色段间长度是可以任意修改的,只需要保证原本的颜色段长度大于 1。
然后就是比较典的插板法了,答案为$\binom{S_0 - 1}{C_0 - 1} \times \binom{S_1 - 1}{C_1 - 1} $。
Code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define inf 1e18
#define eps 1e-9
#define ls 2*k
#define rs 2*k+1
using namespace std;
const int N=1e6+5,M=4e5+5;
const int mod=998244353;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
string s;
int n,cnt[2],sum[2],jc[N],nijc[N];
inline int qpow(int x,int y){
int res=1;
while(y){
if(y&1) res=res*x%mod;
x=x*x%mod,y>>=1;
}
return res%mod;
}
inline int C(int n_,int m_){
if(m_<=0) return 1;
return jc[n_]*nijc[m_]%mod*nijc[n_-m_]%mod;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
jc[0]=nijc[0]=1;
for(int i=1;i<=1e6;i++) jc[i]=jc[i-1]*i%mod,nijc[i]=qpow(jc[i],mod-2)%mod;
int tc;cin>>tc;
while(tc--){
cin>>n>>s; s=" "+s;
for(int i=1;i<=n;i++){
int pos=i,op=s[i]-'0';
while(pos<=n && s[pos]==s[i]) ++pos;
sum[op]+=pos-i,cnt[op]++,i=pos-1;
}
cout<<C(sum[0]-1,cnt[0]-1)*C(sum[1]-1,cnt[1]-1)%mod<<"\n";
sum[0]=sum[1]=0,cnt[0]=cnt[1]=0;
}
return 0;
}
E. Even If the World Turns
link.
我们试图在第一次操作时做一些操作来便于传递出\((x,y)\)。
\(w\)为黑点数量,先考虑\(w = 2\)的情况,此时只有两个黑点,分别记为\((x_1,y_1) , (x_2,y_2)\)。
如果令\(x_1 \times x_2 = x\),那么平移操作时会多乘一倍距离;
如果令\(x_1 + x_2 = x\),那么在平移时也会多加一倍距离,都不容易处理。
但是考虑令\(\frac{x_1 + x_2}{2} = x\),那么平移距离的问题就完美解决了。
推广到任意\(w\)满足\(gcd(w,n) = 1\),都可以用\(\frac{ \sum_{i=1}^{w} x_i}{w} = x\)来处理。
这样前三种操作就可以处理了,对于取反操作,可以钦定数量少的点是黑点。
考虑第一次操作需要构造一次交换,使得\(\frac{ \sum_{i=1}^{w} x_i}{w} = x\)且\(\frac{ \sum_{i=1}^{w} y_i}{w} = y\)。
同时上面这些都是在模\(n\)同余意义下的。
我们会选出一个黑点,记为\((x_1,y_1)\),和一个白点,记为\((x_2,y_2)\),进行交换。
记\(\Delta x = x_2 - x_1\),\(\Delta y = y_2 -y_1\),则\(\frac{ \sum_{i=1}^{w} x_i}{w} + \Delta x = x\)且\(\frac{ \sum_{i=1}^{w} y_i}{w} + \Delta y = y\)。
移项得 \(\Delta x = x - \frac{ \sum_{i=1}^{w} x_i }{ w },\ \Delta y = y - \frac{ \sum_{i=1}^{w} y_i }{ w }\),
这样就可以通过\(\Delta x\)和\(\Delta y\)计算出合法的\((x_1,y_1)\)和\((x_2,y_2)\)。
即满足两个点异色,或者两个点重合。
而计算答案时,可以用 exgcd 求逆元,也可以枚举点判断是否合法(CF官解)。
Code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define inf 1e18
#define eps 1e-9
#define ls 2*k
#define rs 2*k+1
using namespace std;
const int N=2e5+5,M=4e5+5,Q=1e4+5;
const int mod=998244353;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int tc;
char c;
string s;
int x,y,n,w,B;
inline void solve1(){
cin>>tc;
for(int o=1;o<=tc;o++){
cin>>n;
vector< vector<int> >a(n+1,vector<int>(n+1,0));
int cnt=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>c,a[i][j]=(c=='#' ? 1 : 0),cnt+=a[i][j];
cin>>x>>y; x--,y--;
if(cnt<n*n/2) B=1,w=cnt;
else B=0,w=n*n-cnt;
int sumx=0,sumy=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(a[i][j]==B) sumx+=i,sumy+=j;
int deltax=w*x,deltay=w*y;
deltax=(deltax-sumx)%n,deltay=(deltay-sumy)%n;
int fromx=-1,fromy=-1;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(a[i][j]==B){
int tox=((i+deltax)%n+n)%n;
int toy=((j+deltay)%n+n)%n;
if(a[tox][toy]!=B || (tox==i && toy==j)){
fromx=i,fromy=j;
break;
}
}
}
if(fromx!=-1 && fromy!=-1) break;
}
cout<<fromx+1<<" "<<fromy+1<<" "<<((fromx+deltax)%n+n)%n+1<<" "<<((fromy+deltay)%n+n)%n+1<<endl;
}
return ;
}
inline void solve2(){
cin>>tc;
for(int o=1;o<=tc;o++){
cin>>n;
vector< vector<int> >a(n+1,vector<int>(n+1,0));
int cnt=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>c,a[i][j]=(c=='#' ? 1 : 0),cnt+=a[i][j];
int nowc=0;
if(cnt<n*n/2) nowc=1,w=cnt;
else nowc=0,w=n*n-cnt;
int sumx=0,sumy=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(a[i][j]==nowc) sumx+=i,sumy+=j;
int ansx=-1,ansy=-1;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i*w%n==sumx%n && j*w%n==sumy%n){
ansx=i,ansy=j;
break;
}
}
if(ansx!=-1 && ansy!=-1) break;
}
cout<<ansx+1<<" "<<ansy+1<<endl;
}
}
signed main(){
cin>>s;
if(s=="first") solve1();
else solve2();
return 0;
}
F. How Long Until Nothing Remains?
link.
答案具有单调性,考虑二分答案,记为\(w\)。
注意到答案的区间大致在\([n,n+35]\)间。
反着考虑,问题转化为初始时有\(n\)个 0,每次所有数翻倍,可以选一个数+1,最后使得对应位置的数\(\geq a_i\)。
第\(i\)次操作相当于给一个数\(+ 2^{i-1}\)。
当\(w > 30\)时,会有\(w - 30\)个远大于\(a_i\)的 2 的次幂,可以贪心选择去抵消\(a\)中较大的\(w - 30\)个数。
从高到低枚举\(2^{29}\)至\(2^0\),每次贪心选择尚存的最大的数,如果能消灭,直接消灭,否则将这个数减去\(2^i\)。
每次选最大的数,可以用堆维护,而且序列长度很小,也可以直接排序。
时间复杂度:\(O(n \log^2 n)\)。
Code
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define inf 1e18
#define eps 1e-9
#define ls 2*k
#define rs 2*k+1
using namespace std;
const int N=2e5+5,M=4e5+5;
const int mod=998244353;
const int B=32ll;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int n,a[N],now[N];
inline bool check(int w){
for(int i=1;i<=n;i++) a[i]=now[i];
int top=max((w>=B ? n-(w-B) : n),0ll);// 有 w - 30 个 inf
for(int i=min(B-1ll,w-1ll);i>=0;i--){
if(!top) break;
if((1ll<<i)>=a[top] && top) --top;
else a[top]-=(1ll<<i),sort(a+1,a+top+1);
}
return top==0;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int tc;cin>>tc;
while(tc--){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) now[i]=a[i];
int l=n,r=n+50;
while(l<r){
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<"\n";
}
return 0;
}

浙公网安备 33010602011771号