Codeforces 579D "Or" Game【贪心+暴力按位处理】

本文介绍了一个算法问题,探讨如何通过有限次数的乘法操作来最大化一组整数的按位或运算结果。文章提供了详细的解题思路及实现代码。

D. "Or" Game
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given n numbers a1, a2, ..., an. You can perform at most k operations. For each operation you can multiply one of the numbers by x. We want to make as large as possible, where denotes the bitwise OR.

Find the maximum possible value of after performing at most k operations optimally.

Input

The first line contains three integers n, k and x (1 ≤ n ≤ 200 000, 1 ≤ k ≤ 10, 2 ≤ x ≤ 8).

The second line contains n integers a1, a2, ..., an (0 ≤ ai ≤ 109).

Output

Output the maximum value of a bitwise OR of sequence elements after performing operations.

Examples
Input
3 1 2
1 1 1
Output
3
Input
4 2 3
1 2 4 8
Output
79
Note

For the first sample, any possible choice of doing one operation will result the same three numbers 1, 1, 2 so the result is .

For the second sample if we multiply 8 by 3 two times we'll get 72. In this case the numbers will become 1, 2, 4, 72 so the OR value will be 79 and is the largest possible result.


题目大意:

给你N个数,最多可以进行k次操作,每次操作可以选择一个数,使得其变成a【i】*x.

问最终所有数或的值最大是多少。


思路:


1、考虑到或操作的特性:有1的位子上结果就是1.

那么很显然,我们希望得到的结果最大,就要使得最高位尽可能的高。


2、要使得最高位尽可能的高,那么我们希望的就是将K次操作都赋予一个数上,那么这个数一定这个数组中的最大值吗?

显然不是,对于乘法操作中,因为还有*3.*5......这类的操作,就是说在提升最高位的同时,还能使得较小位子上边的1升高。

所以显然不是这个数组中的最大值,那么这个数到底是谁最优呢?

其实我们并不需要太纠结于这个问题。

因为我们可以枚举这个数。

那么我们O(n)枚举出来一个数,将K次操作都赋予这个数上,然后可以通过位压缩的方式,来暴力统计哪些位子上有1.

那么这里可以将O(n^2)的暴力枚举时间复杂度降到O(n*64);


3、细节处理好,注意数据范围。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define ll __int64
ll a[1005000];
ll po[100];
int have[100];
ll poww(ll a,ll b)
{
    return po[b];
}
void add(ll num,int d)
{
    for(int j=0;j<64;j++)
    {
        ll tm=poww(2,j);
        if((num&tm)!=0)
        {
            have[j]+=d;
        }
    }
}
int main()
{
    ll n,k,x;
    while(~scanf("%I64d%I64d%I64d",&n,&k,&x))
    {
        memset(have,0,sizeof(have));
        for(int i=0;i<n;i++)
        {
            scanf("%I64d",&a[i]);
        }
        ll tmp=1;
        for(int i=0;i<64;i++)
        {
            if(i==0)po[i]=1;
            else po[i]=po[i-1]*2;
        }
        for(int i=0;i<k;i++)
        {
            tmp*=x;
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<64;j++)
            {
                ll tm=poww(2,j);
                if((a[i]&tm)!=0)
                {
                    have[j]++;
                }
            }
        }
        ll output=0;
        for(int i=0;i<n;i++)
        {
            add(a[i],-1);
            a[i]*=tmp;
            add(a[i],1);
            ll sum=0;
            for(int j=0;j<64;j++)
            {
                if(have[j]>0)sum+=(ll)poww(2,j);
            }
            output=max(output,sum);
            add(a[i],-1);
            a[i]/=tmp;
            add(a[i],1);
        }
        printf("%I64d\n",output);
    }
}









<think>我们正在处理Codeforces 1775D题目的解析和解题思路。根据用户要求,我们需要参考站内引用,但注意到这些引用可能与题目并不直接相关。不过,我们可以从引用中提取一些思路,比如莫比乌斯反演(引用1提到gcd计数)和优化时间复杂度(引用2提到预处理和更新)的思想。 题目1775D是"Friends and the Restaurant",但根据Codeforces题库,1775D实际上是"Friendly Spiders"。因此,我们需要调整主题。 题目描述(摘要): - 有n个蜘蛛,编号1到n,每个蜘蛛的腿数为a_i。 - 两个蜘蛛i和j是朋友当且仅当gcd(a_i, a_j)>1(即它们的腿数的最大公约数大于1)。 - 需要从蜘蛛s到蜘蛛t,每一步只能移动到朋友蜘蛛,求最短路径(如果存在)。 解题思路: 1. 建图:直接对每对蜘蛛检查gcd会超时(O(n^2))。需要优化。 2. 思路:利用因子分解。对于每个蜘蛛,将其与它的质因数关联。然后,具有相同质因数的蜘蛛可以通过该质因数连接(因为如果两个蜘蛛共享一个质因数,那么它们之间的gcd至少为该质因数(>1))。 3. 具体步骤: a. 预处理每个数的质因数。 b. 创建一张图,包括n个蜘蛛节点和额外的质因数节点(为了避免重复,可以用质因数作为虚拟节点,编号从n+1开始)。 c. 对于每个蜘蛛i,将其与它的每个质因数p(用虚拟节点表示)连接一条无向边(双向边)。 d. 这样,两个蜘蛛如果有公共质因数,就会通过同一个虚拟节点相连(距离为2)。注意,蜘蛛节点之间不直接相连,而是通过质因数节点间接相连。 e. 在这样构建的图上,从s到t的最短路径(以边数计)除以2(因为实际路径中每两个蜘蛛之间经过一个虚拟节点,但虚拟节点不计入答案的路径节点)?注意:我们要求的是蜘蛛节点的路径,但路径中经过的虚拟节点不能出现在最终路径中,因此我们需要在BFS后重构路径。 4. 路径长度计算:由于图中加入了虚拟节点(质因数节点),实际路径长度为(真实路径的边数)/2(因为每两个蜘蛛节点之间隔着1个质因数节点,而质因数节点不算在最终路径里)。但注意,我们要求的是蜘蛛节点的序列,所以最终路径中的节点数 = (整个路径的节点数+1)//2(因为路径是蜘蛛-质因数-蜘蛛-...-蜘蛛,所以蜘蛛节点在奇数位置,且首尾都是蜘蛛)。 5. 具体步骤: - 使用BFS(因为边权为1)从s开始搜索,直到t。注意图中节点包括蜘蛛节点(1到n)和质因数节点(n+1到max_id)。我们需要记录每个节点的前驱节点,以便重构路径。 - 重构路径时,我们只取蜘蛛节点(即跳过质因数节点)。 6. 注意:如果s和t相同,则路径为0(但题目要求从s到t,如果s=t,那么路径长度为0,节点序列只包含s)。 7. 复杂度:每个蜘蛛连接它的质因数(每个数的质因数个数为O(log a_i)),所以总边数O(n*log(max(a_i))),可以接受。 8. 代码实现关键点: - 质因数分解:预处理每个数的质因数(可以不用筛,直接分解)。 - 建图:图的大小为n+质因数节点总数(质因数节点只需要出现一次,所以用一个map来记录质因数对应的虚拟节点编号)。 - BFS:注意节点编号范围。 现在,我们根据上述思路编写解析和代码。 注意:由于虚拟节点的存在,在BFS中,我们记录路径时,虚拟节点不放入最终答案路径。因此,当我们回溯路径时,只保留蜘蛛节点(编号在1到n之间)。 例子:假设蜘蛛1(质因数2,3)和蜘蛛2(质因数2,5)和蜘蛛3(质因数3,5)。 建图: - 蜘蛛1连接质因数2和3。 - 蜘蛛2连接质因数2和5。 - 蜘蛛3连接质因数3和5。 那么蜘蛛1到蜘蛛3的路径:1->2->2(蜘蛛2)->5->3(蜘蛛3)?不对,实际上通过质因数节点: 路径:蜘蛛1 -> 质因数2 -> 蜘蛛2 -> 质因数5 -> 蜘蛛3(这不是最短的,因为还有另一条:蜘蛛1->质因数3->蜘蛛3,更短)。 在图上BFS:从1(蜘蛛1)出发,到达质因数2和3,然后从质因数2可以到蜘蛛2,从质因数3可以到蜘蛛3。所以从1到3的路径:1 -> 质因数3 -> 3(蜘蛛3)。然后重构路径时,我们只取蜘蛛节点:1和3,所以路径长度为1(边数)?不对,实际路径是1-3(中间有一个虚拟节点,但最终路径只包含1和3,所以边数为1,即1->3,但实际上它们不是直接朋友,但通过质因数3连接,所以是朋友关系,路径长度为1步)。 所以最终路径的边数等于(BFS得到的路径边数)除以2?不对,因为BFS路径中每一步都是从一个节点到另一个节点(包括虚拟节点)。例如:1->质因数3->3,这有两条边,但实际蜘蛛节点之间只经过一步(从1到3)。所以最终路径的节点数应该是BFS路径中蜘蛛节点的序列。 在BFS过程中,我们记录每个节点的前驱。当我们到达终点t(蜘蛛节点)时,我们回溯前驱,但跳过虚拟节点。然而,虚拟节点在路径中连接两个蜘蛛节点,所以回溯时,我们每遇到一个虚拟节点,它的前驱和后继都是蜘蛛节点。因此,我们可以这样重构: 路径:t -> 前一个节点(可能是虚拟节点)-> 再前一个节点(蜘蛛节点)-> ... -> s 我们只取蜘蛛节点,并反转。 具体步骤: 1. 从t开始回溯,将t加入路径。 2. 当前节点为t,它的前驱节点记为prev。如果prev是虚拟节点(编号>n),那么继续找prev的前驱(应该是蜘蛛节点)。所以每次回溯一个蜘蛛节点后,下一个节点是虚拟节点,再下一个节点是蜘蛛节点。因此,我们每次回溯两步来跳过虚拟节点?不对,因为虚拟节点和蜘蛛节点交替出现(除了起点和终点是蜘蛛节点)。 实际上,在BFS树中,路径是:s -> p1(质因数节点)-> u1(蜘蛛节点)-> p2(质因数节点)-> u2(蜘蛛节点)-> ... -> t 所以,从t回溯:t的前驱是一个质因数节点,质因数节点的前驱是一个蜘蛛节点(即路径中t的前一个蜘蛛节点)。因此,我们可以: path = [] cur = t while cur != s: path.append(cur) cur = pre[cur] # pre[cur]是当前节点的前驱(在BFS树中) # 此时cur是虚拟节点,那么再取一次前驱得到上一个蜘蛛节点 cur = pre[cur] # 现在cur是上一个蜘蛛节点 path.append(s) path.reverse() 但是注意:从s到第一个虚拟节点,s的前驱是空(因为s是起点)。所以我们需要调整:在BFS过程中,我们记录每个节点的前驱(包括虚拟节点)。然后重构路径时,我们跳过虚拟节点。 另一种方法:在BFS过程中,我们记录每个节点的前驱节点(无论是蜘蛛还是质因数)。然后重构路径时,我们只取蜘蛛节点。那么从t开始,将t加入路径,然后找t的前驱(一定是虚拟节点),再找这个虚拟节点的前驱(一定是蜘蛛节点),将这个蜘蛛节点加入路径,然后继续找这个蜘蛛节点的前驱(虚拟节点)...直到到达s。 步骤: path = [] cur = t while cur is not None: if cur <= n: # 蜘蛛节点 path.append(cur) cur = pre[cur] # 移动到前驱(可能是虚拟节点或None) # 这样得到的是从t到s的路径(逆序),然后反转 path.reverse() 但是这样会包括虚拟节点吗?注意:我们在加入路径时只加入蜘蛛节点(cur<=n),所以虚拟节点被跳过。但是,我们回溯时,每次从蜘蛛节点找到它的前驱(虚拟节点)然后继续再找前驱(上一个蜘蛛节点)?不对,因为pre[t]是一个虚拟节点,然后pre[虚拟节点]是一个蜘蛛节点(即路径中t的前一个蜘蛛节点),然后我们加入这个蜘蛛节点,然后找这个蜘蛛节点的前驱(另一个虚拟节点)...直到s。 所以循环内部: 第一次:cur=t(蜘蛛节点)->加入 然后cur = pre[t] (虚拟节点,不加入) 然后cur = pre[虚拟节点] (上一个蜘蛛节点,在下一次循环加入) 这样会交替加入蜘蛛节点。所以最终路径中只有蜘蛛节点,且顺序正确。 但是,注意起点s:当回溯到s时,s是蜘蛛节点,加入。然后找pre[s],如果s是起点,那么pre[s]应该是None,循环终止。 因此,代码: path = [] cur = t while cur is not None: if cur <= n: path.append(cur) cur = pre[cur] # 移动到前驱(如果cur是s,pre[s]为None,则退出) path.reverse() 这样得到的路径就是蜘蛛节点的序列。 但是,我们如何保证pre[虚拟节点]一定指向蜘蛛节点?在建图时,我们只建立了蜘蛛节点与质因数节点之间的边,没有质因数节点到质因数节点的边。所以图中边只存在于蜘蛛节点和质因数节点之间。因此,在BFS树中,路径一定是交替的:蜘蛛-质因数-蜘蛛-质因数-...-蜘蛛。 因此,上述重构路径方法是正确的。 9. 特殊情况:s==t,则路径只有[s];如果没有路径,则输出-1。 代码实现(Python)可能如下: </think> ### Codeforces 1775D - Friendly Spiders 解析 #### 题目概述 有 $n$ 只蜘蛛,每只蜘蛛 $i$ 有腿数 $a_i$。两只蜘蛛 $i$ 和 $j$ 是“朋友”当且仅当 $\gcd(a_i, a_j) > 1$。给定起点 $s$ 和终点 $t$,求从 $s$ 到 $t$ 的最短路径(每次移动到朋友蜘蛛),并输出路径。若不可达,返回 $-1$。 #### 核心难点 直接暴力建图(检查每对蜘蛛的 $\gcd$)的复杂度为 $O(n^2)$,无法通过 $n \leq 3 \times 10^5$ 的数据规模。 #### 优化思路:质因数分解建图 1. **关键观察**: - 若两只蜘蛛有公共质因数 $p$,则 $\gcd(a_i, a_j) \geq p > 1$,它们可通过 $p$ 间接相连。 - 为每个质因数 $p$ 创建**虚拟节点**(编号从 $n+1$ 开始),蜘蛛 $i$ 与其所有质因数 $p \mid a_i$ 连边。 2. **建图策略**: - **真实节点**:蜘蛛 $1$ 到 $n$。 - **虚拟节点**:质因数 $p$(用 $\text{id}_p \geq n+1$ 表示)。 - **连边规则**: - 蜘蛛 $i$ 连接其所有质因数虚拟节点 $\text{id}_p$(双向边)。 - 例:蜘蛛 $a_i=12$(质因数 $2,3$)连向 $\text{id}_2$ 和 $\text{id}_3$。 3. **路径性质**: - 路径格式:蜘蛛 $\to$ 质因数 $\to$ 蜘蛛 $\to \cdots \to$ 蜘蛛。 - 实际路径长度 = BFS 路径边数 $/ 2$(虚拟节点不计入最终路径)。 #### 算法步骤 1. **预处理质因数**: - 对每个 $a_i$ 分解质因数,用字典记录质因数对应的虚拟节点编号。 2. **BFS 求最短路**: - 从 $s$ 开始 BFS,记录每个节点的前驱 $\text{pre}[u]$。 - 队列同时处理真实节点和虚拟节点。 3. **路径重构**: - 从 $t$ 回溯前驱,只保留真实节点(跳过虚拟节点)。 - 回溯逻辑:若当前是真实节点,加入路径;无论何种节点,均回溯到前驱。 #### 复杂度分析 - **时间**:$O(n \log \max(a_i))$,每个数的质因数分解 $O(\log a_i)$。 - **空间**:$O(n \log \max(a_i))$,存储图及质因数。 #### 代码实现(Python) ```python import math from collections import deque def main(): import sys input = sys.stdin.readline n = int(input()) a = [0] + list(map(int, input().split())) s, t = map(int, input().split()) if s == t: print("1\n", s) return # 质因数虚拟节点编号分配 max_id = n prime_to_id = {} graph = [[] for _ in range(4 * 10 ** 5)] # 预分配空间 # 建图:连接蜘蛛与其质因数 for i in range(1, n + 1): x = a[i] factors = [] # 质因数分解 p = 2 while p * p <= x: if x % p == 0: factors.append(p) while x % p == 0: x //= p p += 1 if x > 1: factors.append(x) for p in factors: if p not in prime_to_id: max_id += 1 prime_to_id[p] = max_id pid = prime_to_id[p] # 双向边:蜘蛛 <-> 质因数 graph[i].append(pid) graph[pid].append(i) # BFS 初始化 pre = [-1] * (max_id + 1) # 前驱数组 q = deque([s]) pre[s] = -2 # 起点标记 # BFS 遍历 while q: u = q.popleft() for v in graph[u]: if pre[v] == -1: # 未访问 pre[v] = u if v == t: q = None # 提前终止 break q.append(v) # 检查是否可达 if pre[t] == -1: print(-1) return # 重构路径(跳过虚拟节点) path = [] cur = t while cur != -2: # 回溯到起点标记 if cur <= n: # 只保留真实节点 path.append(cur) cur = pre[cur] # 输出结果 print(len(path)) print(' '.join(map(str, reversed(path)))) if __name__ == '__main__': main() ``` #### 示例说明 - **输入**:蜘蛛 $a=[2,3,4,5,6]$,$s=1, t=5$。 - **建图**: - 蜘蛛 $1$($a_1=2$)连虚拟节点 $\text{id}_2$。 - 蜘蛛 $5$($a_5=6$)连 $\text{id}_2,\text{id}_3$。 - **路径**:$1 \to \text{id}_2 \to 5$(重构后为 $[1,5]$)。 - **输出**:路径长度 $2$,路径 $1 \space 5$。 #### 相关思考 1. 若存在多条最短路径,如何输出字典序最小路径? 2. 如何进一步优化质因数分解的效率? 3. 若蜘蛛可重复访问,算法如何调整?[^1] [^1]: 解题报告(十八)数论题目泛做(Codeforces 难度:2000 ~ 3000 + ) [^2]: 如果对于每个查询q都找到不同gcd的递减个数,时间复杂度为O(n*q)会超时 [^3]: 如果看到这里,你还没有找到规律,那我们就在看一组数据
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值