码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-04-27 13:36:51      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:直接   ssi   names   连通   opera   \n   operator   ack   dev   

题意:
给一个\(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;
}

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

标签:直接   ssi   names   连通   opera   \n   operator   ack   dev   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!