Codeforces 576D Flights for Regular Customers 矩阵快速幂+DP

题意:
给一个\(n\)点\(m\)边的连通图 每个边有一个权值\(d\) 当且仅当当前走过的步数\(\ge d\)时 才可以走这条边 问从节点\(1\)到节点\(n\)的最短路

好神的一道题 直接写做法喽

首先我们对边按\(d_i\)由小到大排序 设\(f_i\)表示加上\(1\sim i-1\)的所有边走\(d_i\)次后各点间的联通情况 \(G\)表示只连\(1\sim i-1\)的边的邻接矩阵 这些我们可以用一个\(01\)邻接矩阵来存储 则有

\(f_i=f_{i-1}*G^{d_i-d_{i-1}}\)

这很明显是一个矩阵快速幂的过程

之后只需要判断\(1\)与\(n\)之间是否联通 不连通就连下一条边继续判断 否则在当前的范围内二分判断

这样的复杂度还是不够优 我们发现矩阵相乘的过程可以压位后来做 于是将一个矩阵的状态压成\(n\)个\(bitset<n>\) 这样就可过了

我的代码没有压位 而是直接暴力相乘 不过做了点小优化居然就过了~

#include<bits/stdc++.h>
using namespace std;
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define pa pair<int,int>
#define mod 1000000007
#define ll long long
#define mk make_pair
#define pb push_back
#define fi fisrt
#define se second
#define cl(x) memset(x,0,sizeof x)
#ifdef Devil_Gary
#define bug(x) cout<<(#x)<<" "<<(x)<<endl
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define bug(x)
#define debug(...)
#endif
const int INF = 0x7fffffff;
const int N=155;
/*
char *TT,*mo,but[(1<<15)+2];
#define getchar() ((TT==mo&&(mo=(TT=but)+fread(but,1,1<<15,stdin),TT==mo))?-1:*TT++)//*/
inline int read(){
    int x=0,rev=0,ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')rev=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return rev?-x:x;
}
struct data{
    int u,v,t;
    bool operator < (const data&ch){
        return t<ch.t;
    }
}e[N];
int cnt,n,m,tmp[N],sz,bin[32]={1};
struct matrix{
    bool v[N][N];
    matrix operator * (const matrix&b){
        matrix c;cl(c.v);
        for(int j=1;j<=n;j++)
            for(int i=1;i<=n;i++){
                if(!v[j][i]) continue;
                for(int k=1;k<=n;k++)
                    c.v[j][k]|=v[j][i]&&b.v[i][k];
            }
        return c;
    }
}g[N],G,f[N][32];
bool judge(int x){
    int pos=upper_bound(tmp+1,tmp+sz+1,x)-tmp-1,ret=x-tmp[pos];
//  debug("x=%d pos=%d\n",x,pos);
    matrix d=g[pos];
//  bug(d.v[1][n]);
    for(int k=0;k<=30;k++){
        if(bin[k]&ret){
            d=d*f[pos][k];
        }
    }
    return d.v[1][n];
}
int main(){
#ifdef Devil_Gary
    freopen("in.txt","r",stdin);
#endif
    n=read(),m=read();
    for(int i=1;i<=30;i++) bin[i]=bin[i-1]<<1;
    for(int i=1;i<=n;i++) g[1].v[i][i]=1;
    for(int i=1;i<=m;i++){
        e[++cnt].u=read(),e[cnt].v=read(),e[i].t=read();
//      if(!e[i].t) g[1].v[e[i].u][e[i].v]=1;
        tmp[++sz]=e[i].t;
    }
    e[++cnt].u=n,e[cnt].v=n,e[cnt].t=0,tmp[++sz]=0;
    sort(tmp+1,tmp+sz+1);
    sz=unique(tmp+1,tmp+sz+1)-tmp-1;
    sort(e+1,e+cnt+1);
    for(int i=1,j=1;i<=sz;i++){
        for(;e[j].t<=tmp[i]&&j<=cnt;j++){
            G.v[e[j].u][e[j].v]=1;
        }
        f[i][0]=G;
        for(int k=1;k<=30;k++) f[i][k]=f[i][k-1]*f[i][k-1];
        if(i==sz) continue;
        int ret=tmp[i+1]-tmp[i];
        g[i+1]=g[i];
        for(int k=0;k<=30;k++){
            if(bin[k]&ret){
                g[i+1]=g[i+1]*f[i][k];
            }
        }
    }
    int l=0,r=1e9+155;
    while(l<r){
        int mid=l+r>>1;
        if(judge(mid)) r=mid;
        else l=mid+1;
    }
/*  debug("l=%d rr=%d\n",l,tmp[sz]+n+1);*/
    if(l==1e9+155) return puts("Impossible"),0;
    return !printf("%d\n",l);
}

下面这份是压位的做法 我直接粘来的

#include <bits/stdc++.h>
using namespace std;
int n, m;
const int N = 160;
struct edge
{
    int a, b, c;
} E[N];
struct mat
{
    bitset <N> d[N];
} O, I, P, Q;
int comp(edge x, edge y)
{
    return x.c < y.c;
}
mat operator * (mat a, mat b)
{
    mat c;
    for (int i = 1; i <= n; ++ i)
        for (int j = 1; j <= n; ++ j)
            if (a.d[i][j])
                c.d[i] |= b.d[j];
    return c;
}
mat operator ^ (mat a, int b)
{
    mat c = I;
    for (; b; b >>= 1, a = a * a)
        if (b & 1) c = c * a;
    return c;
}
void print(mat a)
{
    for (int i = 1; i <= n; ++ i)
    {
        for (int j = 1; j <= n; ++ j)
            cerr << a.d[i][j] << " ";
        cerr << endl;
    }
}
int res;
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++ i)
        I.d[i][i] = 1;
    for (int i = 1; i <= m; ++ i)
        cin >> E[i].a >> E[i].b >> E[i].c;
    sort(E + 1, E + m + 1, comp);
    E[m + 1].c = E[m].c + n + 5;
    P = I; Q.d[n][n] = 1;
    for (int i = 1; i <= m + 1; ++ i)
    {
        cout<<i<<endl;
        mat tmp = P * (Q ^ (E[i].c - E[i - 1].c));
        if (!tmp.d[1][n])
        {
            Q.d[E[i].a][E[i].b] = 1;
            P = tmp;
            continue;
        }
        res = E[i - 1].c;
        while (!P.d[1][n]) P = P * Q, res ++;
        cout << res << endl;
        return 0;
    }
    cout << "Impossible" << endl;
}

原文地址:https://www.cnblogs.com/devil-gary/p/8960679.html

时间: 2024-10-07 07:09:47

Codeforces 576D Flights for Regular Customers 矩阵快速幂+DP的相关文章

Codeforces 576D Flights for Regular Customers 矩阵快速幂 (看题解)

Flights for Regular Customers 临接矩阵的 k 次 就是 恰好 走 k 步从 i 走到 j 的方案数, 方案数在这里并不关键, 所以可以把它变成01矩阵. 一个很直观的想法是用二分取check它, 但是这并不单调.. 然后就不会了.. 我们可以把G[ n - 1] [ n - 1 ] 变成  1 , 这个函数就变成单调了, 然后直接二分check就好了, 可以用bitset优化一下, 不优化其实也能过. 还有一种方法就是, 每新加入一条边, 我们暴力跑一遍图取chec

bnu 34985 Elegant String(矩阵快速幂+dp推导公式)

Elegant String Time Limit: 1000ms Memory Limit: 65536KB 64-bit integer IO format: %lld      Java class name: Main Prev Submit Status Statistics Discuss Next Type: None None Graph Theory      2-SAT     Articulation/Bridge/Biconnected Component      Cy

VOJ 1067 Warcraft III 守望者的烦恼 (矩阵快速幂+dp)

题目链接 显然可知 dp[n] = dp[n-k] + dp[n-k+1] + ... +dp[n-1]; 然后要用矩阵来优化后面的状态转移. 也就是矩阵 0 1 0 0    a     b 0 0 1 0 * b =  c 0 0 0 1    c     d 1 1 1 1    d    a+b+c+d 然后跑快速幂 #include <iostream> #include <cstdio> #include <algorithm> #include <c

(中等) CF 576D Flights for Regular Customers (#319 Div1 D题),矩阵快速幂。

In the country there are exactly n cities numbered with positive integers from 1 to n. In each city there is an airport is located. Also, there is the only one airline, which makes m flights. Unfortunately, to use them, you need to be a regular custo

Codeforces Round #257 (Div. 2)B 矩阵快速幂

B. Jzzhu and Sequences time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Jzzhu has invented a kind of sequences, they meet the following property: You are given x and y, please calculate fn m

Product Oriented Recurrence(Codeforces Round #566 (Div. 2)E+矩阵快速幂+欧拉降幂)

传送门 题目 \[ \begin{aligned} &f_n=c^{2*n-6}f_{n-1}f_{n-2}f_{n-3}&\\end{aligned} \] 思路 我们通过迭代发现\(f_n\)其实就是由\(c^{x_1},f_1^{x_2},f_2^{x_3},f_3^{x_4}\)相乘得到,因此我们可以分别用矩阵快速幂求出\(x_1,x_2,x_3,x_4\),最后用快速幂求得答案. 对\(f_1,f_2,f_3\): \[ \begin{aligned} (x_n&&

COJ 1208 矩阵快速幂DP

题目大意: f(i) 是一个斐波那契数列 , 求sum(f(i)^k)的总和 由于n极大,所以考虑矩阵快速幂加速 我们要求解最后的sum[n] 首先我们需要思考 sum[n] = sum[n-1] + f(i+1)^k 那么很显然sum[n-1]是矩阵中的一个元素块 那么f(i+1)^k怎么利用f(i) , f(i-1)来求 f(i+1)^k = (f(i) + f(i-1)) ^ k 假如k = 1 , 可以看出f(i+1) = f(i-1) + f(i) (1,1) k = 2 , 可以看出

P1357 花园 (矩阵快速幂+ DP)

题意:一个只含字母C和P的环形串 求长度为n且每m个连续字符不含有超过k个C的方案数 m <= 5  n <= 1e15 题解:用一个m位二进制表示状态 转移很好想 但是这个题是用矩阵快速幂加速dp的 因为每一位的转移都是一样的 用一个矩阵表示状态i能否转移到状态j 然后跑一遍 初试模板题 #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; ll n,

Codeforces 696D Legen...(AC自动机 + 矩阵快速幂)

题目大概说给几个字符串,每个字符串都有一个开心值,一个串如果包含一次这些字符串就加上对应的开心值,问长度n的串开心值最多可以是多少. POJ2778..复习下..太弱了都快不会做了.. 这个矩阵的乘法定义是不同的,m[i][j]=max(m1[i][k]+m2[k][j]),即从i走到k能获得的最大值与从k走到j能获得的最大值之和去更新从i到j能获得的最大值. 另外..关于矩阵内的初始值..用-1表示从i不能到j,比如初始的时候,i不能走一步到j结点这时值就应该设置成-1:而不能用0,因为0是有