(大数mod处理)本题路径值为2^k (k最多500),结果取膜100000 由 (a+b)%m = (a%m +b%m)%m 得(2^a+2^b+·····2^k)0000=(2^a0000+2^b0000 +·····2^k0000)0000
所以得到每条路径0000的值就行,最后相加再取膜100000就行
由a * b % m = (a % m * b % m)%m
2^( k+1)0000=(2^k 0000 * 20000 )0000 =2^k 0000*20000
所以要得到当前路径0000,取上一条路径0000的值乘2再取膜100000
但是因为保存的是取过膜的数没办法比较大小,仔细分析后发现2^k永远大于1+2^1+······+2^(k-1),后一条路会比前面所有路径的总值还大,所以直接保存这是第几条路径作为路径标号,比较时等式两边的集合中的最大路径标号所在方即为较大方,若相等比较次大的,这里可以考虑用set保存一下具体路径。
(…)原本以为求最短路径就是用Dijskstra,可是写到Dijskstra的松弛操作发现居然是一个最小生成树的题。原因就是2^k永远大于1+2^1+······+2^(k-1),那松弛的时候永远选的是标号最小的那条,那直接从最小边开始松弛,可以省去Dijskstra的 d[y]与d[x]+w[x][y]路径的大小判断。于是在生产最小生成树时,只要需要在合并根节点的时候就直接将整个图更新一下就保存了两点间的最短路径。
#include <iostream> #include <stdio.h> using namespace std; #define MAXN 101 #define MAXM 501 #define MAXW 100001 int edge[MAXN][MAXN]; int n,m; int f[MAXN]; int findF (int t){ return f[t] == t ? t : f[t] = findF(f[t]); } void Union(int x, int y){ int xParent = findF(x); int yParent = findF(y); xParent > yParent ? f[xParent] = yParent : f[yParent] = xParent; } int main() { while(cin >> n >> m){ int x,y, dis; for(int i = 0;i < n; i ++){ f[i] = i; edge[i][i] = 0; } for(int i = 0,dis = 1;i < m; i ++ ){ cin >> x >> y; int fx = findF(x) , fy = findF(y); if(fx != fy){ for(int j = 0; j < n; j ++){ if(fx == findF(j)) for(int k = 0; k < n; k ++){ if(fy == findF(k)) edge[j][k] = edge[k][j] = (edge[j][x] + dis + edge[y][k]) % 100000; } } } Union(x,y); dis = dis * 2 % 100000 ; } for(int i=1,root = findF(0); i<n; i++) findF(i) != root ? cout <<"-1" <<endl:cout <<edge[0][i]<<endl; } }