數據範圍:\(1<=n<=10^5,1<=k<=18,1<=k_i<=10^7,\sum |s_i|<=3*10^5\)html
由於\(k\)比較小因此顯然是拿\(k\)來搞事ios
比較簡單粗暴的想法是,咱們直接預處理出每一個徹底圖中的一個點(好吧其實就是團==)到其餘的徹底圖中一個點的最短距離,而後用這個東西來計算兩個點之間的最短路就方便不少了c++
首先能夠用一個\(mp[i][j]\)表示團\(i\)到團\(j\)的最短距離,這個數組在初始化的話直枚舉每一個節點,而後任選這個點所在團中的兩個,取兩個團的邊權的最小值做爲直接鏈接這兩個團的距離,也就是初始的\(mp[i][j]\),而後floyd一下就獲得完整的\(mp\)數組了數組
接下來考慮怎麼用這個玩意計算兩個節點的最短路spa
考慮固定一個起點\(x\),咱們要計算\(x\)到其餘點的最短路,咱們能夠先用預處理出的\(mp\)數組算出\(x\)到任意一個團\(j\)中的一個點(非\(x\))的最短路,記\(dis[j]\),具體一點的話就是枚舉\(x\)先走一步到團\(i\)與團\(j\)的最短路的起點,而後直接走最短路到團\(j\)中某個節點,也就是\(min(k[i]+mp[i][j])\)code
而後\(x\)到\(y\)的最短路必定是\(y\)所屬的團的\(dis\)值的最小值,因此咱們考慮將\(dis\)排序,從小到大計算貢獻,當前團\(j\)的貢獻應該就是\(dis[j]\)乘上當前團中沒有被前面的團所包含的節點的數量htm
如今考慮這個數量怎麼計算blog
由於\(k\)比較小,因此咱們能夠把當前已經考慮完的團具體是哪些給壓成一個二進制數\(nowst\)(爲\(1\)表示尚未考慮),每個原圖中的節點\(x\)所屬的團也壓成一個二進制數\(st[x]\),若是說當前考慮的團中包含的一個節點\(x\)知足\(st[x]|nowst=nowst\),那麼說明這個節點不屬於前面考慮的任何一個團,即有\(1\)的貢獻,因此如今的問題就變成了對於每個團\(i\)咱們要預處理一個\(cnt[i][j]\)表示該團知足\(st[x]|j=j\)的節點\(x\)有多少個,這個東西的計算。。跟某fwt題裏面的操做同樣排序
而後咱們只要枚舉固定的起點\(x\)而後按照上面的步驟計算貢獻就ok了ip
須要注意的是每次計算貢獻的時候由於咱們的\(dis\)處理出來的是\(x\)走到另一個點的距離,也就是意味着咱們在算的時候會把\(x\)到\(x\)的距離算成\(x\)走到排序後第一個團中某個其餘節點的距離,因此須要判一下(減掉就行了),以及最後的答案要除以\(2\)(由於每條邊算了\(2\)次)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=1e5+10,ST=(1<<18)+10; const ll inf=1LL<<60; ll mp[20][20]; int rec[20][N],cnt[20][ST],w[20]; ll dis[20]; int st[N],ord[N]; int n,m; ll ans; int St(int x){return 1<<x-1;} int in(int st,int x){return st>>x-1&1;} void prework(){ for (int i=1;i<=n;++i){ for (int j=1;j<=m;++j){ if (!in(st[i],j)) continue; ++cnt[j][st[i]]; for (int k=1;k<=m;++k) if (in(st[i],k)) mp[j][k]=min(mp[j][k],1LL*w[k]); } } for (int k=1;k<=m;++k) for (int i=1;i<=m;++i){ if (i==k||mp[i][k]==inf) continue; for (int j=1;j<=m;++j){ if (j==k||j==i||mp[k][j]==inf) continue; mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]); } } for (int i=1;i<=m;++i) for (int j=0;j<m;++j) for (int k=0;k<1<<m;++k) if (!(k>>j&1)) cnt[i][k|(1<<j)]+=cnt[i][k]; } bool cmp(int x,int y){return dis[x]<dis[y];} void solve(){ ll tmp; int nowst; for (int x=1;x<=n;++x){ for (int i=1;i<=m;++i) dis[i]=inf,ord[i]=i; for (int i=1;i<=m;++i){ if (!in(st[x],i)) continue; for (int j=1;j<=m;++j) dis[j]=min(dis[j],mp[i][j]+w[i]); } sort(ord+1,ord+1+m,cmp); nowst=(1<<m)-1; tmp=-dis[ord[1]]; for (int i=1;i<=m;++i){ tmp+=dis[ord[i]]*cnt[ord[i]][nowst]; nowst^=St(ord[i]); } ans+=tmp; } printf("%lld\n",ans/2); } int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif int x; scanf("%d%d",&n,&m); for (int i=1;i<=m;++i){ for (int j=1;j<=m;++j) mp[i][j]=i==j?0:inf; scanf("%d%d",&w[i],&rec[i][0]); for (int j=1;j<=rec[i][0];++j){ scanf("%d",&x); rec[i][j]=x; st[x]|=St(i); } } prework(); solve(); }