1. 程式人生 > 其它 >Codeforces Round #730 (Div. 2) 題解

Codeforces Round #730 (Div. 2) 題解

Codeforces Round #730 (Div. 2) 題解

Problem A Exciting Bets

本題有\(t\)組資料。

給出兩個數\(a,b\),進行一次操作可以同時將兩個數增加減少\(1\),設經過\(k\)次操作後的兩個數為\(a',b'\)

求出讓\(gcd(a',b')\)最大值,並求出此最大值條件下最下操作次數\(k\)

注意:如果最大值可以無限大,請輸出\("0\ 0 "\)

對於\(100%\)資料,\(1 \leq t \leq 5 \times 10^3,0 \leq a,b \leq 10^{18}\)

  • 當且僅當\(a=b\)時,最大值可以無限大,輸出\("0\ 0 "\)

下面證明,本題的最大值為\(|a-b|\),不妨設\(0 \leq a\leq b \leq 10^{18}\):

  • \(\max_d \{ gcd(a\pm d,b\pm d)\}=\max_d \{ gcd(b-a,a\pm d)\}=b-a\)

  • 當且僅當\(a\pm d=k(b-a),k\in N\)時成立。

    所以\(|d|_{min}=\min\{a \% (b-a),(b-a)-a\%(b-a)\}\)

時間複雜度為\(O(t)\)

# include <bits/stdc++.h>
# define int long long
using namespace std;
signed main()
{
	int t; cin>>t;
	for (int i=1;i<=t;i++) {
		int a,b; scanf("%lld%lld",&a,&b);
		if (a==b) {printf("0 0\n"); continue;}
		if (a>b) swap(a,b);
		printf("%lld %lld\n",b-a,min(b-a-a%(b-a),a%(b-a)));
	}
	return 0;
}

Problem B Customising the Track

本題有\(t\)組資料。

初始有\(n\)個元素的陣列\(a_n\),可以任意分配數字,但必須保證新陣列各元素和與原陣列各元素的和相等,使得\(\sum_{i=1}^{n}\sum_{j=i+1}^n |a_i-a_j|\)最小。求出該最小值。

對於\(100%\)資料,\(1 \leq t \leq 10^4,0 \leq a_i \leq 10^{9},1 \leq n,\sum n \leq 2\times 10^5\)

最優情況下,陣列會變為\((\sum_{i-1}^{n} a_i) \% n\)\(\lfloor \frac{\sum_{i-1}^{n} a_i}{n} \rfloor +1\)

\(n-(\sum_{i-1}^{n} a_i) \% n\)個$\lfloor \frac{\sum_{i-1}^{n} a_i}{n} \rfloor $組成的陣列。

此時有最小值:\((\sum_{i-1}^{n} a_i) \% n \times (n-(\sum_{i-1}^{n} a_i))\)

時間複雜度為\(O(\sum n + t)\)

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=2e5+10;
int n,a[N];
signed main()
{
	int T; scanf("%lld",&T);
	while (T--){
		int n; scanf("%lld",&n);
		int sum=0;
		for (int i=1;i<=n;i++) {
			int t; scanf("%lld",&t);
			sum+=t;
		}
		sum=sum%n;
		printf("%lld\n",sum*(n-sum));	
	}
	return 0;
}

Problem C Need for Pink Slips

本題有\(t\)組資料。

有編號為\(1,2,3\)的三張卡片,初始摸到的概率分別為\(c,m,p\),給定一個引數\(v\)

若摸到編號為\(3\)的卡片則停止,否則設摸到卡片當前概率為\(a\)

  • \(a\leq v\)則將概率\(a\)平均分給剩下的卡片,並將該卡片從卡池中拿走。

  • \(a>v\)則將從該卡片中抽取的概率\(v\),平均分給剩下的卡片。

求摸到卡片\(3\)的次數的期望值,精確到\(10^{-6}\)

對於\(100\%\)的資料,\(1 \leq t \leq 10,0 < c,m,p < 1,c+m+p=1,0.1 \leq v \leq 0.9\)

注意到本題中,\(0.1\leq v \leq 0.9\),說明最多操作的次數是很有限的。

結合本題的概率期望模型,可以採用在概率樹上\(dfs\)解決,回溯時統計答案。

細節方面,注意\(a\leq v\)的判定並將應當抽出卡池的卡片抽走,防止可能造成的精度誤差。

時間複雜度為\(O(2^k)\)

# include <bits/stdc++.h>
using namespace std;
const double eps=1e-12;
double c,m,p,v,ans;
void dfs(int dep,double cc,double mm,double pp,double pel,bool fcc,bool fmm) {
	if (abs(cc)<eps) fcc=false;
	if (abs(mm)<eps) fmm=false;
	if (fcc) {
		if (cc<=v) {
			if (fmm) dfs(dep+1,0,mm+cc/2.0,pp+cc/2.0,pel*cc,0,fmm);
			else dfs(dep+1,0,0,pp+cc,pel*cc,0,0);
		}
		else {
			if (fmm) dfs(dep+1,cc-v,mm+v/2.0,pp+v/2.0,pel*cc,fcc,fmm);
			else dfs(dep+1,cc-v,0,pp+v,pel*cc,fcc,0);
		}
	}
	if (fmm) {
		if (mm<=v) {
			if (fcc) dfs(dep+1,cc+mm/2.0,0,pp+mm/2.0,pel*mm,fcc,0);
			else dfs(dep+1,0,0,pp+mm,pel*mm,0,0);
		}
		else {
			if (fcc) dfs(dep+1,cc+v/2.0,mm-v,pp+v/2.0,pel*mm,fcc,fmm);
			else dfs(dep+1,0,mm-v,pp+v,pel*mm,fcc,fmm);
		}
	}
	ans=ans+pp*dep*pel;
}
int main()
{
	int T; scanf("%d",&T);
	while (T--) {
		ans=0;
		cin>>c>>m>>p>>v;
		dfs(1,c,m,p,1.0,1,1);
		printf("%.9lf\n",ans); 
	}

	return 0;
}

Problem D RPD and Rap Sheet

本題為互動題,有\(t\)組資料。

定義對\(k\)進位制數運算子 \(A\oplus_{k} B = C\),使得每個\(k\)進位制位\(i\)滿足\(C_i = (A_i+B_i) \% k\)

計算機會給出一個自適應密碼,初始為\(x\),要求猜出當前密碼。

設當前密碼為\(a\),輸入計算機猜測的密碼為\(b\)

  • 若當前猜對,則返回\(1\),本次處理結束。

  • 若當前猜錯,則返回\(0\),自適應密碼將更改為\(c\),並滿足\(a \oplus_{k}c = b\)

要求在\(n\)次嘗試之內猜出密碼,並提交給計算機。

對於\(100\%\)的資料, \(1 \leq t \leq 10^4,1 \leq n,\sum n \leq 2\times 10^5,2 \leq k \leq 100\)

本題有\(Easy \ Version\),當且僅當\(k=2\)時。

\(k=2\)時,\(\oplus_{k=2} \Leftrightarrow \oplus\),考慮性質\(a \oplus b=c \Leftrightarrow a \oplus c = b\)。(兩邊同時亦或上\(a\)得證)

設第\(i\)個詢問輸入到計算機中的數字為\(q_i\),需要滿足當\(i-1\)是初始密碼時,\(i\)是當前密碼。

  • \(q_1=0.\)

  • \(q_i=(i-2) \oplus(i-1) , i\geq 2.\)

    \(i-1\)為初始密碼時,當前密碼為\((i-1)\oplus 0 \oplus(0 \oplus1)...(i-3)\oplus(i-2) = (i-1)\oplus(i-2)=q_i\)

由於初始密碼為\([0,n-1]\)中一個數字,則可以在\(n\)次詢問之內解決問題。

# include <bits/stdc++.h>
using namespace std;
int q(int x) {
	if (x==1) return 0;
	else return (x-1)^(x-2);
}
void fun(int n) {
	int s=0;
	for (int i=1;i<=n;i++) {
		printf("%d\n",q(i));fflush(stdout);
		int r; scanf("%d",&r); 
		if (r==1) return;
	}
}
int main()
{
	int T; scanf("%d",&T);
	while (T--) {
		int n,k; scanf("%d%d",&n,&k);
		fun(n);	
	}
	return 0;
}

\(2 \leq k \leq 100\)時,考慮更一般情況。\(a \oplus_{k}c = b\) 若已知\(k\)進位制數\(a,b\),嘗試求出\(c\)

\(A\oplus_{k} C = B\),使得每個\(k\)進位制位\(i\)滿足\(B_i = (A_i+C_i) \% k\),則\(C_i = (A_i-B_i) \% k\)

定義對\(k\)進位制數運算子 \(A\odot_{k} B = C\),使得每個\(k\)進位制位\(i\)滿足\(C_i = (A_i-B_i) \% k\)

兩個引理:

  • 引理1:\((a \odot_k b)\odot_k(a \odot_k c)=c \odot_k b\)
  • 引理2:\((b \odot_k a)\odot_k(c \odot_k a)=b \odot_k c\)

構造\(q_i\)函式:

  • \(q_1=0.\)
  • \(q_i=(i-2)\odot_k(i-1),i \ is \ even.\)
  • \(q_i=(i-1) \odot_k (i-2), i \ is \ odd.\)

\(x\)為初始密碼時,當前密碼:

  • \(x \odot_k (i-1) , i \ is \ even.\)
  • \((i-1) \odot_k x , i \ is \ odd.\)

證明:

  • \(i \ is \ even.\)

    假設當前密碼為\(x \odot_k (i-1) , i \ is \ even.\)成立,則\(i+1\)時,

    密碼為:\((i \odot_k i-1)\odot_k(x \odot_k i-1)=i \odot_k x\)

  • \(i \ is \ odd.\)

    假設當前密碼為\((i-1) \odot_k x , i \ is \ odd.\)成立,則\(i+1\)時,

    密碼為:\(((i-1) \odot_k i)\odot_k((i-1) \odot_k x)=x \odot_k i\)

所以,當\(i=x\)時,當前答案為:

  • \(x \odot_k (x-1) , x \ is \ even.\)
  • \((x-1) \odot_k x , x \ is \ odd.\)

等於\(q_{x+1}\)

# include <bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=22;
int a[M],b[M],c[M];
int k;
int f(int x,int y) {
	int z=0,p=1;
	while (x>0||y>0) {
		int a=x%k; x=x/k;
		int b=y%k; y=y/k;
		int c=(a-b+k)%k;
		z=z+p*c;
		p=p*k;
	}
	return z;
}
int q(int i) {
	if (i==1) return 0;
	if (i%2==0) return f(i-2,i-1);
	else return f(i-1,i-2);
}
void fun(int n) {
	for (int i=1;i<=n;i++) {
		int r; printf("%d\n",q(i)); fflush(stdout);
		scanf("%d",&r); if (r==1) return;
	}
}
int main()
{
	int T; scanf("%d",&T);
	while (T--) {
		int n; scanf("%d%d",&n,&k);
		fun(n);
	}
	return 0;
}