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;
}
posted @ 2026-08-10 16:02  lmx_rp++  阅读(4)  评论(0)    收藏  举报