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\)
此時有最小值:\((\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;
}