[題解]SFMOI Round I A~C

Sinktank發表於2024-10-04

Portal:https://www.luogu.com.cn/contest/179008

\(\bf{100+50+50+25+5=\color{indianred}225\color{black}\ ,\ rk.\ 184}\)


A - Strange Cake Game

顯然對於小W,向下移動蛋糕刀是最有利的;對於小M,向右移動是最有利的。所以雙方以最佳狀態移動,最終\(x\le y\)的巧克力是小W的。直接統計輸出即可。別忘了開long long

點選檢視程式碼
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,k,sum;
signed main(){
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++){
		int x,y;
		cin>>x>>y;
		if(y>=x) sum++;
	}
	cout<<sum;
	return 0;
}

B1 - Strange Madoka Game

我們第一次提問\(x\),第二次提問\(x+1\)。考慮\(m\)和對方回答的關係。

\(x=6\)舉例,我們把上圖中每個橙色方塊稱作一個區塊。可以發現,第\(1\)個區塊的值與對應的模\(7\)的值相差\(0\),第\(2\)個區塊的值與對應模\(7\)的值相差\(1\)(模\(7\)意義下的),第\(3\)個區塊則相差\(2\)……

所以我們發現規律:記\(m\bmod x=a,m\bmod (x+1)=b\),那麼在區塊大小足夠的前提下(將\(x\)設為一個很大的值即可保證區塊足夠大),\((a-b)\bmod (x+1)\)就是所在區塊\(-1\),自然\(m\)可以表示為\([(a-b)\bmod (x+1)]\times x+a\)。還是注意開long long

賽時沒開long long吃了一發,然後回答沒輸出感嘆號又吃了一發,然後沒清空緩衝區又吃了一發(汗

點選檢視程式碼
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t;
const int x=399999999;
signed main(){
	cin>>t;
	int a,b;
	while(t--){
		cout<<"? "<<x<<endl;
		cin>>a;
		cout<<"? "<<x+1<<endl;
		cin>>b;
		int chunk=(a-b+x+1)%(x+1);
		cout<<"! "<<chunk*x+a<<endl;
	}
	return 0;
}

B2 - Strange Homura Game

這個題也簡單。先提問一個很大的\(x\)(比如\(10^{18}\)),假設對方回答為\(a\),那麼再詢問\(x-a-1\),假設回答為\(b\),那麼\(m=b+1\)

這是因為\(x\bmod m=a\),所以\((x-a-1)\bmod m=(m-1)\)

點選檢視程式碼
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t;
signed main(){
	cin>>t;
	int a,b;
	while(t--){
		cout<<"? 1000000000000000000"<<endl;
		cin>>a;
		cout<<"? "<<1000000000000000000-a-1<<endl;
		cin>>b;
		cout<<"! "<<b+1<<endl;
	}
	return 0;
}

C - Strange Train Game

真的好題,思路很巧妙。

我們把所有操作放在一張\(n+1\)個節點的無向圖上,將所有\(l_i\)\(r_i +1\)連邊。我們有一個結論:
能夠交換區間\([l,r]\)而不對其他下標產生影響,當且僅當\(l\)\(r+1\)在一個連通塊中。
這是因為\(l\)\(r+1\)在同一連通塊中,所以存在\(l\)\(r+1\)的路徑,這條路徑可能會經過超出\([l,r+1]\)邊,但這種

比如下圖,\((1,3),(1,7),(8,10),(6,10)\)這些操作所連成的邊處於一個連通塊中,那麼我們可以交換區間\([4,5]\)而不影響其他下標。

所以我們可以貪心考慮每一個位置\(i\),如果\(a_i=b_i\),就忽略掉,否則就找到\(i\)所在連通塊中最大的節點\(nxt\),如果\(nxt=i\),就說明無法修改當前點,否則貪心地將\([i,nxt-1]\)的所有位置都進行操作。

為什麼每次操作\([i,nxt-1]\)能保證正確性?因為\(nxt-1\)是連通塊中最大節點,所以如果\(i\)所在連通塊的其他節點\(j\)也需要修改,就會把\([j,nxt-1]\)再進行翻轉,就抵消掉了。

點選檢視程式碼
#include<bits/stdc++.h>
#define N 200010
#define int long long
using namespace std;
int n,m,fa[N];
bool op[N];
string a,b;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void merge(int u,int v){
	u=find(u),v=find(v);
	if(u<v) fa[u]=v;
	else if(u>v) fa[v]=u;
}
signed main(){
	cin>>n>>m>>a>>b;
	a=' '+a,b=' '+b;
	for(int i=1;i<=n;i++){
		if(a[i]==b[i]) fa[i]=i+1;
		else fa[i]=i;
	}
	fa[n+1]=n+1;
	while(m--){
		int l,r;
		cin>>l>>r;
		merge(l,r+1);
	}
	bool cur=0;
	for(int i=1;i<=n;i++){
		if(a[i]==b[i]) continue;
		cur^=op[i],a[i]^=cur;
		int nxt=find(i);
		if(a[i]=='0'&&nxt>i){
			a[i]='1',cur^=1,op[nxt]^=1;
		}
	}
	cout<<a.substr(1,n);
}

相關文章