2020-11-28 洛谷月赛 LGR-080 解题报告

2020-11-28 洛谷月赛 LGR-080 解题报告

前排先膜出题人sooke!!!

由于我还不会E和F,先把前四题(div.2四题)写了。

先放个总链接

比赛链接

【LGR-080】洛谷 11 月月赛 II div.1 - 洛谷

【LGR-080】洛谷 11 月月赛 II div.2 - 洛谷

A 双生独白

题目链接

P7106 双生独白 - 洛谷

题目大意

输入一个16进制颜色编码,输出其“反色”的16进制编码。

反色即: ( R , G , B ) → ( 255 − R , 255 − G , 255 − B ) (R,G,B)\to (255-R, 255-G, 255-B) (R,G,B)(255R,255G,255B)

解题报告

做法很多,我直接用printfscanf解决了这题!

关键代码

scanf("#%2x%2x%2x", &a, &b, &c);
a = 255 - a, b = 255 - b, c = 255 - c;
printf("#%02X%02X%02X\n", a, b, c);

我们稍加分析。

第1行的%2x是:限制宽度为2,读入一个16进制数;

第3行的%02X是:若宽度小于2则用0补到2,输出一个不含前导0x的16进制数,且字母使用大写。(这是x与X的差别)

那么这题就水过去了。

B 天选之人

题目链接

P7107 天选之人 - 洛谷

题目大意

构造一组 x i ( 1 ≤ i ≤ n ) x_i(1\le i\le n) xi(1in),满足以下条件:

  • x i ∈ [ 0 , m ] x_i\in[0,m] xi[0,m]
  • ∑ i = 1 n x i = k \sum_{i=1}^nx_i=k i=1nxi=k
  • n n n x i x_i xi中有且仅有 p p p个最大值

Hint

  • 1 ≤ p , n ≤ 1 0 5 , 1 ≤ m ≤ 1 0 9 , 0 ≤ k ≤ n m 1\le p,n\le 10^5, 1\le m\le 10^9, 0\le k\le nm 1p,n105,1m109,0knm

解题报告

首先一个很显然的贪心,我们这 p p p个最大值应该尽可能大。这样的话,一是后面需要选的数少了,二是后面的数可以选的范围也更宽松了。

于是我们的最大值应该取 M x = min ⁡ { m , ⌊ k p ⌋ } Mx=\min\{m,\lfloor\dfrac{k}{p}\rfloor\} Mx=min{m,pk}

那么剩下的 r e s = k − M x res=k-Mx res=kMx分给剩下 n − p n-p np个数。

于是再贪心地选择,每个数尽量大。那么都选 min ⁡ { M x − 1 , r e s } \min\{Mx-1,res\} min{Mx1,res}。到最后查看 r e s res res是否为0即可。

时间是 O ( n ) O(n) O(n)其实好像可以O(1)​的

代码就不放了。

C 移花接木

论我是怎么丢掉这题的分的。。没考虑 b = 1 b=1 b=1的情况。

1 ≤ a , b ≤ 1 0 9 , 0 ≤ h ≤ 1 0 9 1\le a, b\le 10^9,0\le h\le 10^9 1a,b109,0h109

题目链接

P7108 移花接木 - 洛谷

题目大意

把一棵高度为 ∞ \infty 的满 a a a叉树变为高度为 h h h的满 b b b叉树,有以下两种操作可以使用:

  1. 删去一棵子树
  2. 让一棵子树成为另一个节点的儿子( 必须仍保持树的形态)

求最小操作次数,对 1 0 9 + 7 10^9+7 109+7取余。

  • 1 ≤ a , b ≤ 1 0 9 , 0 ≤ h ≤ 1 0 9 1\le a,b\le10^9,0\le h\le 10^9 1a,b109,0h109

解题报告

PART1: a ≥ b a\ge b ab时,我们不增加子树,只砍掉子树。这时我们发现只用操作1就可以了,操作2没有任何的好处。

对于 0 ⋯ h − 1 0 \cdots h-1 0h1层的节点,我们需要砍掉 ( a − b ) (a-b) (ab)个子树。显然我们应该从上往下砍。这样以后,第0层砍掉了 ( a − b ) (a-b) (ab),第1层还剩 b b b个节点,需要砍 b ( a − b ) b(a-b) b(ab)。一直到第 h − 1 h-1 h1层还剩 b h − 1 b^{h-1} bh1个节点,需要砍 b h − 1 ( a − b ) b^{h-1}(a-b) bh1(ab)。最后 h h h层全部要砍光,即 b h ⋅ a b^h\cdot a bha

最后答案为

( a − b ) ( 1 + b + b 2 + ⋯ + b h − 1 ) + b h ⋅ a (a-b)(1+b+b^2+\cdots+b^{h-1})+b^h\cdot a (ab)(1+b+b2++bh1)+bha

= ( a − b ) 1 − b h 1 − b + b h ⋅ a =(a-b)\dfrac{1-b^{h}}{1-b}+b^h\cdot a =(ab)1b1bh+bha

快速幂+逆元计算。注意 b = 1 b=1 b=1的情况!!!

PART2: a < b a<b a<b时,我们应当多多地用操作2。为了减少次数,应选择第 h h h层节点的子树往上移动。容易发现,一个子树往上移,又会多出好多 h h h层的节点,又有许多的子树往上移。所以我们发现:这些子树是移不完的!

于是我们贪心地先一换一,把子树全部往上填,填完了再删去第 h h h层节点的全部子树。最后答案就是 b h ⋅ a b^h\cdot a bha

D 滴水不漏

题目链接

P7109 滴水不漏 - 洛谷

题目大意

交互题。有 n n n个杯子,编号为 i i i的杯子的容积为 i i i,且一开始装有 a i ( 0 ≤ a i ≤ i ) a_i(0\le a_i\le i) ai(0aii)的水量(你并不知道)。每次你可询问 ( i , j ) (i,j) (i,j)。若 i = j i=j i=j,表示询问 i i i有没有满(1/0);若 i ≠ j i\ne j i=j,表示把 i i i里的水往 j j j倒,直到 j j j满或 i i i空,再告诉你 j j j有没有满(1/0)。

  • 2 ≤ n ≤ 1000 2\le n\le 1000 2n1000,交互次数 ≤ 20000 \le20000 20000

解题报告

我们设当前的水量为 b i b_i bi

我们看看倒一次水会发生啥:比如 p → q p\to q pq,只有两种情况:

  • q q q满了,那么我们知道了 b q = q b_q=q bq=q,然而我们就不知道 b p b_p bp了。
  • q q q没满,那么我们知道了 b p = 0 b_p=0 bp=0,然而我们还是不知道 b q b_q bq

有了这个性质,我们就应当用已知推出未知。

显然我们不能在两个未知水量的杯子之间倒水,搞得不好就再也不知道水量了。我们也没必要在已知水量的杯子之间倒来倒去浪费次数。我们每次操作必须在一个未知杯子和已知杯子之间倒水。但是我们发现,这样倒来倒去,未知数并没有减少,问题本质一样的。

所以我们寻求一种手段,能够直接把一个未知变为已知。一般情况下,我们可以把 未知数规约到1号杯子,再通过一次1 1的询问就可以得到这个时候1号杯子的值!

考虑怎么规约问题。一种很明显的方法就是二进制拆分,即把当前杯子(假设编号为 p p p)与小于它的2的整幂发生关系,然后一通神仙操作(详细请看别的博客或sooke的课件)得到新的 b 1 ⋯ b p b_1\cdots b_p b1bp。由于我们是顺序地求解,现在 1 ⋯ p 1\cdots p 1p杯子之间只会互相发生关系,那么通过“物质守恒定律”,我们有 ∑ i = 1 p a i = ∑ i = 1 p b i \sum_{i=1}^pa_i=\sum_{i=1}^pb_i i=1pai=i=1pbi,可以得到 a p = ∑ i = 1 p b i − ∑ i = 1 p − 1 a i a_p=\sum_{i=1}^pb_i-\sum_{i=1}^{p-1}a_i ap=i=1pbii=1p1ai

详细代码

const int MAXN = 1005;
int n, b[MAXN], a[MAXN], lg2[MAXN];
int ask(int i, int j) {//pour from i into j
	printf("? %d %d\n", i, j); fflush(stdout);
	return read();
}
void calc(int p) {
	int ans = 0;
	for(int i = 1; i <= p; i++) ans += b[i];
	for(int i = 1; i < p; i++) ans -= a[i];
	a[p] = ans;
}
void solve(int p, int ty = 0) { //calculate a[p](if ty = 1) & b[p]; sure that b[p] < p
	if(p == 1) {
		b[p] = 0;
		if(ty) calc(p);
		return ;
	}
	for(int i = lg2[p-1]; i >= 0; i--) {
		int q = 1 << i;
		if(b[q] == 0) continue;
		int t = ask(q, p);
		if(t) {//p is full
			b[p] = p;
			solve(q);//now q is unsure
			if(ty == 1) calc(p);
			return ;
		} else {//q is empty
			b[q] = 0;
		}
	}
	
	//now b[2^0],b[2^1]... is all empty, and p is not full yet
	for(int i = lg2[p-1]; i >= 0; i--) {
		int q = 1 << i;
		int t = ask(p, q);
		if(t) {//q is full
			b[q] = q;
		} else {//p is empty
			b[p] = 0;
			solve(q);
			if(ty == 1) calc(p);
			return ;
		}
	}
	b[p] = 0;
	if(ty == 1) calc(p);
}
int main() {
	n = read();
	lg2[0] = -1;
	for(int i = 1; i <= n; i++) lg2[i] = lg2[i >> 1] + 1;
	for(int i = 1; i <= n; i++) {
		int t = ask(i, i);
		if(t) {
			a[i] = b[i] = i;
			continue;
		}
		solve(i, 1);
	}
	printf("! ");
	for(int i = 1; i < n; i++) printf("%d ", a[i]);
	printf("%d\n", a[n]);
	fflush(stdout);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日居月诸Rijuyuezhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值