9.17 第二题

题意

给出了一个\(N\)个点\(M\)条边的有向图
求有多少个有序点对\((a,b)\),满足至少存在一个点\(c\)以及从\(c\)\(a\)的一条路径\(L_a\)\(c\)
\(b\) 的一条路径\(L_b\),使得\(L_a\)的长度是\(L_b\)的两倍
注意不一定是简单路径

\(N,M\leq 3\times10^3\)

解法

考虑对于点\(c\),一个合法的二元组是怎样形成的

我们可以看成是由点\(a,b\)\(c\)开始,点\(a\)走一步,点\(b\)走两步这样不断行走而形成的

我们把这个过程拆成三个阶段:

  • \(a\)向其出边走一步
  • \(b\)向其出边走一步
  • \(b\)向其出边走一步

对于任意一组合法的方案我们进行上述操作,一定都能得到一组新的合法的方案

我们考虑记忆化搜索

\(f[a][b][0]\)为合法状态,\(f[a][b][1]\)为第一阶段的状态,\(f[a][b][2]\)为第二阶段的状态。当到达第三阶段时,我们又获得一个合法状态,于是又用\(f[a][b][0]\)存储

考虑记忆化搜索的方式,我们用广搜不断扩展合法状态,类似今年省选\(\tt {D2T1}\)校园路径

最后统计答案累加即可

一共只有\(3N^2\)个状态,所以最后的时间复杂度是\(O(3N^2)\)

代码

#include <cstdio>
#include <queue>

using namespace std;

const int N = 3010;

int read();

struct node { 
    int u, v, w; 
    node(int _u, int _v, int _w) : u(_u), v(_v), w(_w) {}
};

int n, m;

int cap;
int head[N], nxt[N], to[N];

int f[N][N][3];

queue<node> que;

inline void add(int x, int y) { 
    to[++cap] = y, nxt[cap] = head[x], head[x] = cap; 
}


int main() {
    
    n = read(), m = read(); 
    for (int i = 1; i <= m; ++i) {
        int x = read(), y = read();
        add(x, y);
    }
    
    for (int i = 1; i <= n; ++i)  que.push(node(i, i, 0));
    for (int i = 1; i <= n; ++i)  f[i][i][0] = 1;
    
    while (!que.empty()) {
        node top = que.front(); que.pop();
        int u = top.u, v = top.v, w = top.w;
        if (!w) {
            for (int i = head[u]; i; i = nxt[i])
                if (!f[to[i]][v][1])  
                    f[to[i]][v][1] = 1, que.push(node(to[i], v, 1));
        } else {
            w = (w + 1) % 3;
            for (int i = head[v]; i; i = nxt[i])
                if (!f[u][to[i]][w])    
                    f[u][to[i]][w] = 1, que.push(node(u, to[i], w));
        }
    }
    
    int ans = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            ans += f[i][j][0];
    
    printf("%d\n", ans);
    
    return 0;
}

int read() {
    int x = 0, c = getchar();
    while (c < '0' || c > '9')   c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x;   
}

转载于:https://www.cnblogs.com/VeniVidiVici/p/11537541.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值