IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Codeforces Round #730 div.2 A-E题解 -> 正文阅读

[数据结构与算法]Codeforces Round #730 div.2 A-E题解

视频讲解:TBD

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| a?b
证明:
设所有操作后, 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=k1??g

b + x = k 2 ? g b+x=k_2*g b+x=k2??g

a ? b = ( k 1 ? k 2 ) g a-b=(k_1-k_2)g a?b=(k1??k2?)g

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

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+g?1???g?a
x < 0 x < 0 x<0 ,则 ∣ x ∣ = a ? ? a g ? ? g |x|=a- \lfloor \frac{a}{g} \rfloor \cdot g x=a??ga???g
最小步数为其中的较小值。

参考代码

#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(1n2?105) 个轨道,编号从 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=1n?j==i+1n?ai??aj?

题解

观察表达式,易得 a i a_i ai? 尽可能平均分布时最优。
s u m = ∑ i = 1 n a i sum=\sum_{i=1}^n{a_i} sum=i=1n?ai? 不一定能等分为 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=sum?ave?n 辆车。
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 n?last 个轨道有 a v e ave ave 辆车。
答案 a n s = l a s t ? ( n ? l a s t ) ans=last \cdot (n-last) ans=last?(n?last)

参考代码

#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 ak?b 表示整数 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(1n2?105) 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(0xn?1) ,你需要在最多 n n n 次交互内,猜出这个整数。
猜测时,输出猜测的整数 y ( 0 ≤ y ≤ 2 ? 1 0 7 ) y(0 \leq y \leq 2 \cdot 10^7) y(0y2?107) ,系统会视情况返回不同的整数 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 xk?z=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 a?k?b 表示 k k k 进制下不退位的 a ? b a-b a?b ,其与 ⊕ k \oplus _k k? 都可以通过手写加减法的形式实现。
那么如果猜测错误后, x x x 变为 z z z ,则可以表示为 z = y ? k x z=y \ominus_k x z=y?k?x
设第 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?=yi??k?ansi?1?

初始 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?=yi??k?(yi?1??k?(yi?2??k?...?k?x))

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?={yi??k?yi?1?k?yi?2??...?k?xyi??k?yi?1?k?yi?2??...k?x?x%2=1x%2=0?

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

x x x 逐个从 0 0 0 枚举到 n ? 1 n-1 n?1 进行询问时,即 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(i?1)?k?ii?(i?1)?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 n?1 维的超立方体,其中一个的节点编号是从 0 0 0 2 n ? 1 ? 1 2^{n-1}-1 2n?1?1 另一个的节点编号是从 2 n ? 1 2^{n-1} 2n?1 2 n ? 1 2^n-1 2n?1 。(简单 0 0 0 维超立方体是一个点)
  • 对于 0 ≤ i < 2 n ? 1 0 \leq i <2^{n-1} 0i<2n?1 中的每一个整数 i i i ,在节点 i i i 和节点 i + 2 n ? 1 i+2^{n-1} i+2n?1 之间连边。

定义置换 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} n?2n?1 条边;
  • 每个节点有 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 n?1 。对于满足 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,...,2n?1 。具体哪个节点对应哪个编号均可。

接下来 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} bn?1?bn?2?...b0? ,那么其节点颜色为 ⊕ i = 0 n ? 1 i ? b i \oplus_{i=0}^{n-1}{i \cdot b_i} i=0n?1?i?bi?

合理性证明:

  • 对于一个节点 u = b n ? 1 b n ? 2 . . . b 0 u=b_{n-1}b_{n-2}...b_{0} u=bn?1?bn?2?...b0? 来说,其颜色为 c = ⊕ i = 0 n ? 1 i ? b i c=\oplus_{i=0}^{n-1}{i \cdot b_i} c=i=0n?1?i?bi?
  • 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...u2n?1} ,对应的颜色集为 { c ⊕ 0 , c ⊕ 1... c ⊕ ( n ? 1 ) } \{c \oplus 0, c \oplus 1...c \oplus (n-1)\} {c0,c1...c(n?1)} ,恰好为 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("");
	}
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章           查看所有文章
加:2021-07-11 16:51:09  更:2021-07-11 16:53:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:37:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码