Codeforces Round #730 div.2 A-E题解

视频讲解:BV19g411u7P6

A. Exciting Bets

题目大意

给定整数 a , b ( 0 ≤ a , b ≤ 1 0 18 ) a,b(0 \leq a,b \leq 10^{18}) a,b(0a,b1018) ,可以对其修改任意次数,使得 g c d ( a , b ) gcd(a,b) gcd(a,b) 最大。求修改后的 g c d ( a , b ) gcd(a,b) gcd(a,b) 与最小修改次数。
每次修改时,可以将 a , b a,b a,b 都增加 1 1 1 ,或减少 1 1 1 (只有当 a , b > 0 a,b>0 a,b>0 时才能减少)。
g c d ( a , b ) gcd(a,b) gcd(a,b) 可以无穷大,则输出 “0 0” 。
x ≥ 0 x\geq 0 x0 时,认为 g c d ( x , 0 ) = x gcd(x,0)=x gcd(x,0)=x

题解

a = b a=b a=b ,则 g c d ( a , b ) gcd(a,b) gcd(a,b) 可以无穷大。反之,最大的 g c d ( a , b ) gcd(a,b) gcd(a,b) ∣ a − b ∣ |a-b| ab
证明:
设所有操作后, a , b a,b a,b 增加了 x x x ,变为 a + x , b + x a+x,b+x a+x,b+x g c d ( a + x , b + x ) = g gcd(a+x,b+x)=g gcd(a+x,b+x)=g ,则
a + x = k 1 ∗ g a+x=k_1*g a+x=k1g

b + x = k 2 ∗ g b+x=k_2*g b+x=k2g

a − b = ( k 1 − k 2 ) g a-b=(k_1-k_2)g ab=(k1k2)g

可得 g g g a − b a-b ab 的因子,最大就是 ∣ a − b ∣ |a-b| ab ,证毕。

x ≥ 0 x \geq 0 x0 ,则 x = ⌊ a + g − 1 g ⌋ ⋅ g − a x=\lfloor \frac{a+g-1}{g}\rfloor \cdot g -a x=ga+g1ga
x < 0 x < 0 x<0 ,则 ∣ x ∣ = a − ⌊ a g ⌋ ⋅ g |x|=a- \lfloor \frac{a}{g} \rfloor \cdot g x=agag
最小步数为其中的较小值。

参考代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

int main()
{
	ll T,a,b,g,ad1,ad2;
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld%lld",&a,&b);
		if(a==b)
		{
			printf("0 0\n");
			continue;
		}
		g=abs(a-b);
		ad1=(a+g-1)/g*g-a;
		ad2=a-a/g*g;
		printf("%lld %lld\n",g,min(ad1,ad2));
	}
}

B. Customising the Track

题目大意

n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1 \leq n \leq 2 \cdot 10^5) n(1n2105) 个轨道,编号从 1 1 1 n n n 。第 i i i 个轨道上有 a i a_i ai 辆车。
你可以执行任意次修改,每次修改可以将一辆车移动到另一个轨道上。
求修改后的最小 ∑ i = 1 n ∑ j = = i + 1 n ∣ a i − a j ∣ \sum_{i=1}^n{\sum_{j==i+1}^{n}{|a_i-a_j|}} i=1nj==i+1naiaj

题解

观察表达式,易得 a i a_i ai 尽可能平均分布时最优。
s u m = ∑ i = 1 n a i sum=\sum_{i=1}^n{a_i} sum=i=1nai 不一定能等分为 n n n 份。设每个轨道上有 a v e = ⌊ s u m n ⌋ ave=\lfloor \frac{sum}{n} \rfloor ave=nsum 辆车,则还剩余 l a s t = s u m − a v e ⋅ n last=sum-ave \cdot n last=sumaven 辆车。
l a s t last last 辆车放置在任意轨道中,每个轨道放置一辆,则有 l a s t last last 个轨道有 a v e + 1 ave+1 ave+1 辆车,有 n − l a s t n-last nlast 个轨道有 a v e ave ave 辆车。
答案 a n s = l a s t ⋅ ( n − l a s t ) ans=last \cdot (n-last) ans=last(nlast)

参考代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

int main()
{
	ll T,n,x,sum,i,last,ans;
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld",&n);
		sum=0;
		for(i=1;i<=n;i++)
		{
			scanf("%lld",&x);
			sum+=x;
		}
		last=sum-sum/n*n;
		ans=last*(n-last);
		printf("%lld\n",ans);
	}
}

C. Need for Pink Slips

题目大意

有三种奖品,分别简称为C奖品,M奖品,P奖品。每次获取奖品时,会随机从中获得一项,概率分别为 c , m , p ( 0 < c , m , p < 1 , c + m + p = 1 ) c,m,p(0< c,m,p < 1,c+m+p=1) c,m,p(0<c,m,p<1,c+m+p=1)
其中P奖品是目标奖品,玩家会不断抽奖,直到抽到了P奖品。
抽奖有软保底机制。每次抽奖后,会根据波动参数 v ( 0.1 ≤ v ≤ 0.9 ) v(0.1 \leq v \leq 0.9) v(0.1v0.9) 调整三种奖品的获得概率:

  • 若抽到了P奖品,则直接结束。
  • 否则。假设抽到的奖品的当前概率为 a a a,则:
    • a ≤ v a \leq v av ,则将该奖品从奖池中删除,今后不会再抽到该奖品,即获奖概率调整为 0 0 0 。降低的获奖概率,将平分到奖池中的其他奖品上;
    • a > v a > v a>v ,则将该奖品的获奖概率减少 v v v 。降低的获奖概率,将平分到奖池中的其他奖品上;

求抽中P奖品的抽奖次数期望。

题解

f ( c , m , p ) f(c,m,p) f(c,m,p) 表示当前获奖概率分别为 c , m , p c,m,p c,m,p 时的抽奖次数期望,那么根据题意,可以写出其递归方程式(详见参考代码)。
因为 v ≥ 0.1 v \geq 0.1 v0.1 ,所以递归层数最多为 d = 11 d=11 d=11 ,时间复杂度为 O ( 2 d ) O(2^d) O(2d) ,在时限内,因此直接暴力模拟即可。
由于是浮点数运算,需要额外注意精度问题。

参考代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const double eps=1e-10;

double dfs(double c,double m,double p,double v)
{
	double newc,ret=1;
	for(int i=0;i<2;i++,swap(c,m))
	{
		if(c<eps)
			continue;
		newc=max(0.0,c-v);
		if(m<eps)
			ret+=c*dfs(newc,m,p+newc,v);
		else
			ret+=c*dfs(newc,m+(c-newc)/2,p+(c-newc)/2,v);
	}
	return ret;
}

int main()
{
	int T;
	double c,m,p,v;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lf%lf%lf%lf",&c,&m,&p,&v);
		printf("%.10f\n",dfs(c,m,p,v));
	}
}

D1+D2. RPD and Rap Sheet

题目大意

定义 a ⊕ k b a \oplus_k b akb 表示整数 a a a b b b 的在 k k k 进制下的不进位加法。特别的,当 k = 2 k=2 k=2 时,这一操作可以称为位运算的异或。
给定整数 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1 \leq n \leq 2 \cdot 10^5) n(1n2105) k ( 2 ≤ k ≤ 100 ) k(2 \leq k \leq 100) k(2k100) ,现在有一个隐藏的整数 x ( 0 ≤ x ≤ n − 1 ) x(0 \leq x \leq n-1) x(0xn1) ,你需要在最多 n n n 次交互内,猜出这个整数。
猜测时,输出猜测的整数 y ( 0 ≤ y ≤ 2 ⋅ 1 0 7 ) y(0 \leq y \leq 2 \cdot 10^7) y(0y2107) ,系统会视情况返回不同的整数 r r r

  • x = y x=y x=y ,则 r = 1 r=1 r=1
  • 若 $ x \neq y$ ,则 r = 0 r=0 r=0 ,且 x x x 会变成 z z z ,满足 x ⊕ k z = y x \oplus _k z=y xkz=y

对于 Easy Version, k = 2 k=2 k=2
对于 Hard Version, 2 ≤ k ≤ 100 2 \leq k \leq 100 2k100

题解

a ⊖ k b a \ominus _k b akb 表示 k k k 进制下不退位的 a − b a-b ab ,其与 ⊕ k \oplus _k k 都可以通过手写加减法的形式实现。
那么如果猜测错误后, x x x 变为 z z z ,则可以表示为 z = y ⊖ k x z=y \ominus_k x z=ykx
设第 i i i 次猜测的输出为 y i y_i yi ,猜测后的隐藏数为 a n s i ans_i ansi ,则具有以下式子:
a n s i = y i ⊖ k a n s i − 1 ans_i=y_i \ominus_k ans_{i-1} ansi=yikansi1

初始 a n s 0 = x ans_0=x ans0=x ,可以得到通项式:
a n s i = y i ⊖ k ( y i − 1 ⊖ k ( y i − 2 ⊖ k . . . ⊖ k x ) ) ans_i =y_i \ominus_k (y_{i-1} \ominus_k (y_{i-2} \ominus_k ...\ominus_k x)) ansi=yik(yi1k(yi2k...kx))

a n s i = { y i ⊖ k y i − 1 ⊕ k y i − 2 ⊖ . . . ⊖ k x x % 2 = 1 y i ⊖ k y i − 1 ⊕ k y i − 2 ⊖ . . . ⊕ k x x % 2 = 0 ans_i = \begin{cases} y_i \ominus_k y_{i-1} \oplus_k y_{i-2} \ominus ... \ominus_k x &x\%2=1 \\ y_i \ominus_k y_{i-1} \oplus_k y_{i-2} \ominus ... \oplus_k x &x\%2=0 \end{cases} ansi={yikyi1kyi2...kxyikyi1kyi2...kxx%2=1x%2=0

因此可以在 [ 0 , n − 1 ] [0,n-1] [0,n1] 范围内枚举 x x x ,根据历史 y i y_i yi ,求得当前应该询问的数 y i = a n s i − 1 y_i=ans_{i-1} yi=ansi1

x x x 逐个从 0 0 0 枚举到 n − 1 n-1 n1 进行询问时,即 y i + 1 y_{i+1} yi+1 x = i x=i x=i 时的 a n s i ans_i ansi ,上式也可以化简为:
y i + 1 = a n s i = { 0 i = 0 ( i − 1 ) ⊖ k i i % 2 = 1 i ⊖ ( i − 1 ) i % 2 = 0 y_{i+1}=ans_{i}= \begin{cases} 0 &i=0 \\(i-1) \ominus_k i &i\%2=1 \\i \ominus (i-1) &i\%2=0 \end{cases} yi+1=ansi=0(i1)kii(i1)i=0i%2=1i%2=0

就代码实现难度而言。是否进行化简区别不是特别大。

对于 Easy Version , ⊕ 2 \oplus_2 2 ⊖ 2 \ominus_2 2 都可以用异或进行计算。
对于 Hard Version , ⊕ k \oplus_k k ⊖ k \ominus_k k 需要手写不进位加法与不退位减法。

参考代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

int k;

int addk(int a,int b)
{
	int base,aw,bw,ret=0;
	for(base=1;base<=max(a,b);base*=k)
	{
		aw=a/base%k;
		bw=b/base%k;
		ret+=(aw+bw)%k*base;
	} 
	return ret;
}

int subk(int a,int b)
{
	int base,aw,bw,ret=0;
	for(base=1;base<=max(a,b);base*=k)
	{
		aw=a/base%k;
		bw=b/base%k;
		ret+=(aw-bw+k)%k*base;
	} 
	return ret;
}

int main()
{
	int T,n,y,i,r;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&k);
		for(i=0;i<n;i++)
		{
			if(i==0)
				y=0;
			else if(i&1)
				y=subk(i-1,i);
			else
				y=subk(i,i-1);
			printf("%d\n",y);
			fflush(stdout);
			scanf("%d",&r);
			if(r==1)
				break;
		}
	}
}

E. The Final Pursuit

题目大意

定义简单 n n n 维超立方体可以表示为一个无向无权图,其采用以下递归方式构建:

  • 获取两个简单 n − 1 n-1 n1 维的超立方体,其中一个的节点编号是从 0 0 0 2 n − 1 − 1 2^{n-1}-1 2n11 另一个的节点编号是从 2 n − 1 2^{n-1} 2n1 2 n − 1 2^n-1 2n1 。(简单 0 0 0 维超立方体是一个点)
  • 对于 0 ≤ i < 2 n − 1 0 \leq i <2^{n-1} 0i<2n1 中的每一个整数 i i i ,在节点 i i i 和节点 i + 2 n − 1 i+2^{n-1} i+2n1 之间连边。

定义置换 n n n 维超立方体由对简单 n n n 维超立方体的节点按任意方式重新排列得到。
置换 n n n 维超立方体除了节点编号不同外,其结构和简单 n n n 维超立方体相同,都具有以下性质:

  • 2 n 2^n 2n 个节点;
  • n ⋅ 2 n − 1 n \cdot 2^{n-1} n2n1 条边;
  • 每个节点有 n n n 条边;
  • 没有自环或重边;

置换 n n n 维超立方体可以用排列 P P P 对简单 n n n 维超立方体的节点编号置换后得到。
现在给定用无向图表示的置换 n ( 1 ≤ n ≤ 16 ) n(1 \leq n \leq 16) n(1n16) 维超立方体,求排列 P P P ,并对每个节点按以下方式染色:

  • 共有 n n n 种不同的颜色,编号为 0 0 0 n − 1 n-1 n1 。对于满足 0 ≤ u < 2 n 0 \leq u < 2^n 0u<2n 的每个节点 u u u 和每个满足 0 ≤ c < n 0 \leq c <n 0c<n 的每个颜色 c c c ,至少有一个顶点 v v v u u u 相邻,且颜色为 c c c

题解

这个问题包含两个子问题,一个是求置换 P P P ,另一个是顶点染色。

求置换 P

根据简单 n n n 维超立方体的定义,我们可以得到推论:

  1. 当且仅当两个节点的编号只相差一个二进制位时,它们才有连边。
  2. 若两个节点 a , b a,b a,b 的编号相差两个二进制位,则它们在图的距离为 2 2 2 ,且恰好有 2 2 2 个节点位于 a , b a,b a,b 之间。

不妨初始设 p 0 = 0 p_0=0 p0=0 ,对于 0 0 0 号节点的每个相邻节点,均可视为新维度的拓展,标记其简单编号为 1 , 2 , 4 , . . . , 2 n − 1 1,2,4,...,2^{n-1} 1,2,4,...,2n1 。具体哪个节点对应哪个编号均可。

接下来 i i i 从小到大贪心求解 p i p_i pi

  • i i i 2 k 2^k 2k ,则其在标记 0 0 0 号节点的相邻节点时已被标记过。
  • 否则由于推论 2 2 2 ,必定可以构造两个简单编号 b 1 , b 2 ( b 1 , b 2 < i ) b1,b2(b1,b2<i) b1,b2(b1,b2<i) ,满足其与简单节点 i i i 距离为 1 1 1 ,他们之间的距离为 2 2 2 。这样,只需要在输入图上寻找与 p b 1 p_{b_1} pb1 p b 2 p_{b_2} pb2 都相邻且距离为 1 1 1 的点,标记其原始节点为 i i i 即可。

寻找与 p b 1 p_{b_1} pb1 p b 2 p_{b_2} pb2 都相邻且距离为 1 1 1 的点时,可以枚举 p b 1 p_{b_1} pb1 的相邻点集,用二分或 set 在 p b 2 p_{b_2} pb2 的相邻点集中快速检查是否存在。

顶点染色

关于顶点染色部分,推荐参考 3Blue1Brown 的 不可能的棋盘谜题 问题介绍与 Stand-up Maths 的解法
具体证明不再赘述,这里给出结论:

  • 只有当 n n n 2 2 2 的整数次幂时才有解;
  • 简单 n n n 维超立方体上,一种最基础的构造方式是 u u u 的相邻 c c c 颜色节点的编号是 u ⊕ c u \oplus c uc

根据以上结论,我们可以尝试如下构造节点颜色:

  • 设节点编号 v v v 用二进制表示为 b n − 1 b n − 2 . . . b 0 b_{n-1}b_{n-2}...b_{0} bn1bn2...b0 ,那么其节点颜色为 ⊕ i = 0 n − 1 i ⋅ b i \oplus_{i=0}^{n-1}{i \cdot b_i} i=0n1ibi

合理性证明:

  • 对于一个节点 u = b n − 1 b n − 2 . . . b 0 u=b_{n-1}b_{n-2}...b_{0} u=bn1bn2...b0 来说,其颜色为 c = ⊕ i = 0 n − 1 i ⋅ b i c=\oplus_{i=0}^{n-1}{i \cdot b_i} c=i=0n1ibi
  • u u u 的相邻点集可以表示为 { u ⊕ 2 0 , u ⊕ 2 1 . . . u ⊕ 2 n − 1 } \{u\oplus 2^0,u\oplus 2^1...u\oplus 2^{n-1}\} {u20,u21...u2n1} ,对应的颜色集为 { c ⊕ 0 , c ⊕ 1... c ⊕ ( n − 1 ) } \{c \oplus 0, c \oplus 1...c \oplus (n-1)\} {c0,c1...c(n1)} ,恰好为 n n n 种不同颜色。

参考代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const int MAXN=1<<17;
vector<int> e[MAXN];
set<int> st[MAXN];
int p[MAXN],rp[MAXN],c[MAXN];

int main()
{
	int T,n,vNum,eNum,i,j,x,y,v,b1,b2;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		vNum=1<<n;
		eNum=n<<(n-1);
		for(i=0;i<vNum;i++)
		{
			e[i].clear();
			st[i].clear();
			p[i]=rp[i]=-1;
		}
		for(i=0;i<eNum;i++)
		{
			scanf("%d%d",&x,&y);
			e[x].push_back(y);
			e[y].push_back(x);
			st[x].insert(y);
			st[y].insert(x);
		}
		p[0]=rp[0]=0;
		x=1;
		for(i=0;i<e[0].size();i++)
		{
			y=e[0][i];
			p[x]=y;
			rp[y]=x;
			x<<=1;
		}
		for(i=1;i<vNum;i++)
		{
			if(i==(i&-i))
				continue;
			b1=i^(i&-i);
			b2=i^(b1&-b1);
			for(j=0;j<e[p[b2]].size();j++)
			{
				v=e[p[b2]][j];
				if(rp[v]==-1&&st[p[b1]].count(v))
				{
					p[i]=v;
					rp[v]=i;
				}
			}
		}
		for(i=0;i<vNum;i++)
			printf("%d ",p[i]);
		puts("");
		if(n!=(n&-n))
		{
			printf("-1\n");
			continue;
		}
		for(i=0;i<vNum;i++)
		{
			c[i]=0;
			for(j=0;j<n;j++)
			{
				if(i&(1<<j))
					c[i]^=j;
			}
		}
		for(i=0;i<vNum;i++)
			printf("%d ",c[rp[i]]);
		puts("");
	}
}
  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值