Codeforces 576D Flights for Regular Customers 矩陣快速冪+DP
阿新 • • 發佈:2018-04-27
ons lse tin pan air sort \n uniq 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