contest地址
要有梦想,万一过了呢
A. Exciting Bets
题意简述
T组询问,每组询问给定a,b,每次操作可以对a,b同时加上1或同时减去1,求问通过这种操作可以使gcd(a,b)最大为多少,以及达到这个最大值所需要的最小操作次数
若gcd(a,b)可以为无穷大,则输出0 0
T≤5⋅103
0≤a,b≤1018
解法
显然,当a=b时,应用以上操作可以使gcd(a,b)达到无穷大
当a=b时,不妨a>b,根据更相减损术,gcd(a,b)=gcd(a−b,b)
在对a,b同时增减的过程中a−b的值不变,而b是可以改变的
又因为gcd(x,y)≤min{x,y},因此gcd(a,b)≤a−b
而根据更相减损术的方法,我们只需要让b成为a−b的倍数即可令gcd(a,b)=a−b
复杂度O(1)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <iostream> #include <cmath> #include <cstdlib> #include <algorithm> #include <cstring> #include <cstdio> using namespace std; typedef long long ll;
template<typename T>void read(T &t) { t=0;int f=0;char c=getchar(); while(!isdigit(c)){f|=(c=='-');c=getchar();} while(isdigit(c)){t=t*10+c-'0';c=getchar();} if(f)t=-t; }
int T; ll a,b;
void Work() { read(a),read(b); if(a<b)swap(a,b); ll c=a-b; if(c==0) { printf("0 0\n"); return; } ll s=b/c; printf("%lld %lld\n",c,min(b-s*c,(s+1)*c-b)); }
int main() { read(T); while(T--) { Work(); } return 0; }
|
B. Customising the Track
题意简述
T组询问,每组询问给定长度为n的序列{an}
每次操作可以选定任意一个ai,将其减去v≤ai,并使任意一个aj+v(i=j)
通过若干次这样的操作,最小化∑i=1n∑j=i+1n∣ai−aj∣,输出这个最小值
解法
显然,我们只需要让所有的ai尽量接近即可,由于sum=∑ai不变,所以只需要令ai=⌊nsum⌋或⌈nsum⌉,使得∑ai仍为sum即可
那么答案即为(n−summodn)⋅(summodn)
即下取整的数的个数乘上取整的数的个数
复杂度O(n)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include <iostream> #include <cmath> #include <cstdlib> #include <algorithm> #include <cstring> #include <cstdio> using namespace std; typedef long long ll;
template<typename T>void read(T &t) { t=0;int f=0;char c=getchar(); while(!isdigit(c)){f|=(c=='-');c=getchar();} while(isdigit(c)){t=t*10+c-'0';c=getchar();} if(f)t=-t; }
const int maxn=200000+100; int T; int n; ll a[maxn];
void Work() { read(n); ll sum=0; for(int i=1;i<=n;++i) { read(a[i]); sum+=a[i]; } ll plus=sum%n; printf("%lld\n",(n-plus)*plus); }
int main() { read(T); while(T--) { Work(); } return 0; }
|
C. Need for Pink Slips
我是伞兵
打比赛的时候明明留意到了这个问题的,复杂度算错了
题意简述
T组询问
有三个操作C M P
,定义c,m,p为选择对应操作的概率,以及一个参数v
如果选定了操作P
,则操作结束
否则,在选定了操作C
或操作M
之一后,被选定的操作对应的概率减少v,如果该概率已经小于v,那么它将变为0。与此同时,减少的概率将被等量地平分加给剩下的其他有效操作的概率,使得总概率总为1。
概率变为0的操作将变成无效操作,它对应的概率在之后的操作中不会参与平分减少的概率
例如:
- (c,m,p)=(0.2,0.1,0.7),v=0.1,选定操作
M
,使得(c,m,p)=(0.25,Invalid,0.75)
- 再选择操作
C
,使得(c,m,p)=(0.15,Invalid,0.85)
求操作次数的期望值
T≤10
0≤c,m,p≤1,c+m+p=1
0.1≤v≤0.9
精度误差ϵ≤10−6
解法
注意到v≥0.1,因此操作次数最多为10次
考虑深度优先搜索,由于一旦选定操作P
,操作结束,因此每次的决策只有选择操作C
和选择操作M
,直到p=1为止
使用深度优先搜索遍历每种情况,复杂度为O(2v1)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| #include <iostream> #include <cmath> #include <cstdlib> #include <algorithm> #include <cstring> #include <cstdio> using namespace std; typedef long long ll;
template<typename T>void read(T &t) { t=0;int f=0;char c=getchar(); while(!isdigit(c)){f|=(c=='-');c=getchar();} while(isdigit(c)){t=t*10+c-'0';c=getchar();} if(f)t=-t; }
const double eps=1e-6; int T; double c,m,p,v; double ans;
void DFS(double step,double c,double m,double p,double cur) { ans+=step*cur*p; if(fabs(p-1)<eps) return; double dec; if(c>eps) { dec=min(c,v); if(m>eps) DFS(step+1,c-dec,m+dec/2,p+dec/2,cur*c); else DFS(step+1,c-dec,0,p+dec,cur*c); } if(m>eps) { dec=min(m,v); if(c>eps) DFS(step+1,c+dec/2,m-dec,p+dec/2,cur*m); else DFS(step+1,0,m-dec,p+dec,cur*m); } }
void Work() { scanf("%lf%lf%lf%lf",&c,&m,&p,&v); ans=0; DFS(1,c,m,p,1); printf("%.10f\n",ans); }
int main() { read(T); while(T--) { Work(); } return 0; }
|
Tips!
要有梦想!!!
这题写出来了不是能上大分?(
D1. RPD and Rap Sheet (Easy Version)
题意简述
这是一道交互题
定义a⨁Kb表示a,b在K进制下的不进位加法,显然当K=2时,⨁2即为异或操作
T组询问
每组询问给定n,K,要求通过不多于n次尝试猜一个在区间[0,n−1]内的整数密码x
在每一次输出询问后,将通过标准输入流输入一个数r。r=1表示答案正确,进入下一组询问;r=0表示答案错误,同时x将变为z,满足x⨁Kz=y,其中y是询问的数。
T≤10000
1≤n≤2⋅105
在简单版本中K=2
解法
那就嗯猜就好了嘛,从0一直猜到n−1
但密码是一直在变的,所以我们要设法将错误查询对原始密码的影响抵消掉
(约定:以下⨁与⨁2等价)
我们知道x⨁x=0(异或自反性)
由于异或即二进制下的不进位加法,那么它也满足交换律和结合律
于是有x⨁z=y⇒x⨁y⨁z⨁y=z⨁z⨁y⇒x⨁y=z
一种简单的想法是,我们可以在每次错误查询后再使用同一个数字进行一次查询,这样就可以抵消掉错误操作的影响,但是这需要2n次操作
考虑将抵消操作和查询操作同时进行,假设上次错误查询的数为y′,查询前密码为x,查询后为z,现在想要尝试y。
如果恰好y=x,有
z=x⨁y′=y⨁y′
如果y=x, 在完成本次查询后,密码z′满足
z′=z⨁y⨁y′=x⨁y′⨁y⨁y′=x⨁y
所以在查询时,只需要输出y⨁y′。假如y与初始密码相等,那么y⨁y′就等于当前密码;假如y与初始密码不相等,那么当前密码受y′的影响也被抵消,仅受y影响
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <iostream> #include <cmath> #include <cstdlib> #include <algorithm> #include <cstring> #include <cstdio> using namespace std; typedef long long ll;
template<typename T>void read(T &t) { t=0;int f=0;char c=getchar(); while(!isdigit(c)){f|=(c=='-');c=getchar();} while(isdigit(c)){t=t*10+c-'0';c=getchar();} if(f)t=-t; }
int T; int n,K;
void Work() { read(n),read(K); printf("0\n"); fflush(stdout); int r; read(r); if(r==1) return; for(int i=1;i<n;++i) { printf("%d\n",i^(i-1)); fflush(stdout); read(r); if(r==1) return; } }
int main() { read(T); while(T--) { Work(); } return 0; }
|