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;
}
posted @ 2018-04-27 08:01  Devil_Gary  阅读(186)  评论(0编辑  收藏  举报