Codeforces 821E Okabe and El Psy Kongroo【Dp+矩阵快速幂】

本文探讨了一个复杂的路径寻找问题,从点(0,0)到点(K,0),在特定条件下,考虑到N段平行于X轴的线段限制,求解所有可能的行走方案数。文章介绍了使用动态规划和矩阵快速幂的解决方案,详细阐述了如何优化大数值下的状态转移过程。

E. Okabe and El Psy Kongroo
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
Okabe likes to take walks but knows that spies from the Organization could be anywhere; that's why he wants to know how many different walks he can take in his city safely. Okabe's city can be represented as all points (x, y) such that x and y are non-negative. Okabe starts at the origin (point (0, 0)), and needs to reach the point (k, 0). If Okabe is currently at the point (x, y), in one step he can go to (x + 1, y + 1), (x + 1, y), or (x + 1, y - 1).

Additionally, there are n horizontal line segments, the i-th of which goes from x = ai to x = bi inclusive, and is at y = ci. It is guaranteed that a1 = 0, an ≤ k ≤ bn, and ai = bi - 1 for 2 ≤ i ≤ n. The i-th line segment forces Okabe to walk with y-value in the range 0 ≤ y ≤ ci when his x value satisfies ai ≤ x ≤ bi, or else he might be spied on. This also means he is required to be under two line segments when one segment ends and another begins.

Okabe now wants to know how many walks there are from the origin to the point (k, 0) satisfying these conditions, modulo 109 + 7.

Input
The first line of input contains the integers n and k (1 ≤ n ≤ 100, 1 ≤ k ≤ 1018) — the number of segments and the destination x coordinate.

The next n lines contain three space-separated integers ai, bi, and ci (0 ≤ ai < bi ≤ 1018, 0 ≤ ci ≤ 15) — the left and right ends of a segment, and its y coordinate.

It is guaranteed that a1 = 0, an ≤ k ≤ bn, and ai = bi - 1 for 2 ≤ i ≤ n.

题目大意:

我们现在位于(0,0)处,目标是走到(K,0)处。

每一次我们都可以从(x,y)走到(x+1,y-1)或者(x+1,y)或者(x+1,y+1)三个位子之一。

现在一共有N段线段,每条线段都是平行于X轴的。

我们如果此时x是在这段线段之内的话,我们此时走到的点(x,y)需要满足0<=y<=Ci.

现在保证一段线段的终点,一定是下一段线段的起点。

问我们从起点走到终点的行走方案数。

思路:

看到数据范围,很套路的Dp+矩阵快速幂。

而且这个题的Dp转移方程不难写,其实看起来这个E比D简单的多= =

假设我们此时只有一段线段且K很小,那么我们可以设定Dp【i】【j】表示走到点(i,j)的方案数。

那么很显然Dp【i】【j】+=Dp【i-1】【j】+Dp【i-1】【j-1】+Dp【i-1】【j+1】;

初始化dp【0】【0】=1即可。

然而这里K很大,那么我们对于每一段的状态转移过程使用矩阵快速幂优化一下即可。

设定矩阵的时候,我们就可以忽略掉X轴这个维度,设定dp【i】去转移即可:

过程维护上一次到达线段终点的可行方案数,然后继续转移下一段线段。

Wa在了第七组下,已经标注上Wa点了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>

using namespace std;

int n;
long long k;
long long mod=1e9+7;

struct node
{
    long long v[20][20];
};

node mul(node a,node b)
{
    node ans;
    for(int i=0;i<=15;i++)
        for(int j=0;j<=15;j++)
        {
            long long tans=0;
            for(int k=0;k<=15;k++)
            {
                tans=(tans+a.v[i][k]*b.v[k][j])%mod;
            }
            ans.v[i][j]=tans;
        }
    return ans;
}

node powd(node a,long long b)
{
    node ans;
    for(int i=0;i<=15;i++)
        for(int j=0;j<=15;j++)
            ans.v[i][j]=(i==j);

    while(b>0)
    {
        if(b%2==1)
            ans=mul(ans,a);
        a=mul(a,a);
        b/=2;
    }
    return ans;
}
int main()
{
    while(~scanf("%d%lld",&n,&k))
    {
        node ans;
        for(int i=0;i<=15;i++)
            for(int j=0;j<=15;j++)
            ans.v[i][j]=0;

        ans.v[0][0]=1;
        for(int l=1;l<=n;l++)
        {
            long long a,b;
            int c;
            scanf("%lld%lld%d",&a,&b,&c);
            if(l==n)
                b=k;

            long long d=(b-a);
            for(int i=c+1;i<=15;i++)
                ans.v[0][i]=0;      //attention!

            node tp;
            for(int i=0;i<=15;i++)
                for(int j=0;j<=15;j++)
                    tp.v[i][j]=0;

            for(int i=0;i<=c;i++)
            {
                if(i-1>=0)
                    tp.v[i-1][i]=1;
                tp.v[i][i]=1;
                tp.v[i+1][i]=1;
            }
            ans=mul(ans,powd(tp,d));
        }
        printf("%lld\n",ans.v[0][0]);
    }
}

 

### Codeforces CF821D Okabe and City 的 01 BFS 解法 #### 题目概述 题目描述了一个 \( n \times m \) 的网格城市,其中某些单元格由路灯照亮。从左上角 (1, 1) 开始,目标是到达右下角 (\( n, m \))。允许的操作包括: - 移动到相邻的已点亮单元格。 - 如果当前所在单元格为点亮状态,则可以选择临时点亮其所在的整行或整列(代价为 1)。 最终目的是找到从起点到终点的最小代价路径[^5]。 --- #### 使用 01 BFS 的原因 本题中的边权分为两种情况: - **边权为 0**:当两个点亮的单元格在四个方向之一上相邻时。 - **边权为 1**:当两个点亮的单元格可以通过一次性点亮中间的一行或多列来连接时。 这种特殊的边权分布非常适合使用 01 BFS 来解决。相比传统的 Dijkstra 算法,01 BFS 更加高效,因为它利用双端队列(deque)实现,在处理边权仅为 0 和 1 的情况下具有线性时间复杂度。 --- #### 实现细节 以下是基于上述分析的具体实现方法: 1. **建图逻辑** - 将所有的点亮单元格作为节点。 - 对于任意两个点亮单元格 \( u \) 和 \( v \),如果它们满足以下条件之一,则建立一条边: - 它们在同一行或同一列,并且距离小于等于 2(即通过一次操作可以覆盖的距离),此时边权为 1。 - 它们直接相邻(曼哈顿距离为 1),此时边权为 0。 2. **特殊边界处理** - 若终点 (\( n, m \)) 不是一个点亮的单元格,则将其视为虚拟节点,与最近的一个点亮单元格相连,边权固定为 1。 3. **算法流程** - 初始化一个 deque 并将起点加入队列,设置初始距离为 0。 - 使用 dist 数组记录每个节点的最短距离。 - 每次从队首取出节点并更新与其相邻的所有节点的距离: - 若新距离更优,则根据边权决定放入队首还是队尾: - 边权为 0 放入队首; - 边权为 1 放入队尾。 4. **终止条件** - 当首次访问到终点时返回对应的最短距离;若遍历结束后仍未到达终点,则说明不可达。 --- #### Python 实现代码 ```python from collections import deque def okabe_and_city(lit_cells, n, m): # 坐标映射至编号 pos_to_id = {} id_to_pos = [] for idx, (r, c) in enumerate(lit_cells): pos_to_id[(r, c)] = idx id_to_pos.append((r, c)) adj_list = [[] for _ in range(len(pos_to_id))] # 构造邻接表 for i in range(len(id_to_pos)): r1, c1 = id_to_pos[i] for j in range(i + 1, len(id_to_pos)): r2, c2 = id_to_pos[j] dr, dc = abs(r1 - r2), abs(c1 - c2) if dr == 0 or dc == 0: # 同一行或一列 if max(dr, dc) <= 2: weight = 1 elif min(dr, dc) == 1: # 相邻 weight = 0 else: continue adj_list[i].append((j, weight)) adj_list[j].append((i, weight)) # 添加虚拟终点 end_r, end_c = n, m if (end_r, end_c) not in pos_to_id: virtual_end = len(adj_list) id_to_pos.append((end_r, end_c)) for node_idx in range(len(pos_to_id)): r1, c1 = id_to_pos[node_idx] if abs(end_r - r1) + abs(end_c - c1) == 1: adj_list[node_idx].append((virtual_end, 1)) adj_list[virtual_end].append((node_idx, 1)) start_node = pos_to_id.get((1, 1), None) target_node = pos_to_id.get((n, m), virtual_end) if start_node is None: return -1 # 01-BFS INF = float('inf') distance = [INF] * len(adj_list) queue = deque() distance[start_node] = 0 queue.append(start_node) while queue: current = queue.popleft() curr_dist = distance[current] for neighbor, edge_weight in adj_list[current]: new_dist = curr_dist + edge_weight if new_dist < distance[neighbor]: distance[neighbor] = new_dist if edge_weight == 0: queue.appendleft(neighbor) else: queue.append(neighbor) return distance[target_node] if distance[target_node] != INF else -1 # 输入样例解析 if __name__ == "__main__": n, m, k = map(int, input().split()) lit_cells = [tuple(map(int, input().split())) for _ in range(k)] result = okabe_and_city(lit_cells, n, m) print(result) ``` --- #### 复杂度分析 - 时间复杂度:\( O(E) \),其中 \( E \) 是构建的图中总边数。对于每一对点亮单元格最多只考虑一次,因此总体复杂度可控。 - 空间复杂度:\( O(V + E) \),存储节点和边的信息。 --- #### 结论 以上方法充分利用了 01 BFS 的特性,解决了带特定权重约束的最短路问题。这种方法不仅效率高,而且易于理解和实现。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值