NOI2019 I 君的商店

I 君的商店

不知道这道题是怎么被人想出来的,tql

题意:

有一张图,(n) 个点,(m) 条边,但是你不知道这张图具体的模样,你可以执行 4 种操作来确定这张图:

  • 每个点初始有一个状态 (0)
  1. ( m modify(x)),修改点 (x) 的状态以及其所有与其有边的点的状态。
  2. ( m query(x)),查询点 (x) 的状态。
  3. ( m report(x,y)),告诉交互库存在一条 (x o y) 的无向边,并储存之。
  4. ( m check(x)),检查 (x) 的所有出边是否均被储存。

其中 (1,2,4) 操作均有次数限制,令 (L_m,L_q,L_c) 依次表示其操作次数限制。

部分分:

  1. 暴力即可。
  2. (Nle 2cdot 10^5,M=N/2,L_m,L_qle 18N,L_c=0)
  3. (Nle 2cdot 10^5,M=N-1,L_m,L_qle 18N),保证为链。
  4. (3),保证为树。
  5. 保证为内向树。
  6. 一张图,(Nle 2cdot 10^5,Mle 3cdot 10^5,L_q,L_mle 50N)
  • (2) 类数据

考虑按照二进制位进行拆位处理,如果当前位是 (1) 那么修改它,然后依次查询。

如果这个点连的点在当前位不同,那么其状态一定改变。

否则一定不变,那么可以直接算出来这个点的对应点。

  • (3) 类数据

发现做法 (2) 实际上可以得到这个点连出去的点的编号的异或和。

(0) 号的出边,这个可以 (mathcal O(n)) 确定,接下来可以直接递推两边的结果。

  • (4) 类数据

考虑如何在树上确定,类似 (2) 的处理,接下来我们仍然先处理 (0) 的出边是谁,考虑如果知道了叶子节点是谁,那么根据我们得到的信息可以轻易的还原树。

所以每次修改 (x),然后查询 (f_x) 是否改变即可得到所有的叶子节点。

大概是一个剥叶子的思路。

  • (5) 类数据

注意到父亲是单调的,考虑二分一个 (x),然后将 ([0,x)) 均进行修改,那么如果 (uin [x,n)) 的状态改变了,则其与这个区间存在连边。

对于所有点都需要求,显然可以换成整体二分。

  • (6) 类数据

我们先随机一个排列,如果这个点往在它前面的点连边数为奇数,那么设这个点为关键点。

可以证明,关键点的数量期望为 (frac{n}{3})

我们考虑一个算法,使得每轮可以至少找出关键点向前连边的一条边。

考虑二分一个 (x),然后依次操作 (a_{0sim x-1}),接着查询所有点,显然处于后面的点如果状态改变,说明其往前面的集合至少有一条边。

然后将二分换成类似于整体二分的处理,注意到如果一个集合与你的连边为偶数,那么在处理这个集合的时候我们将所有点都进行 modify 对你依然没有影响,不过当然也可以暴力点修改后再改回来,于是显然这样可以筛出 (frac{n}{3}) 条边,这玩意儿的查询/修改次数显然为 (T(n)=mathcal O(nlog n) +T(frac{2n}{3})=mathcal O(3nlog n))

当然,我们显然在单次操作时要去除已操作的边的影响,这样才可以保证单次至少可以筛出 (frac{n}{3}) 条其他边。

通过 check 操作进行适当剪枝可以在 (mathcal O(50N)) 的查询通过本题。

原文地址:https://www.cnblogs.com/Soulist/p/13653778.html