大概做法是这样的
考虑最朴素的做法,预处理出1到所有点的最短路数组dis1和方案数数组cnt1,和预处理出n到所有点的最短路数组dis2和方案数数组出cnt2,然后暴力枚举点对(A,B),如果A和B之间没有连边,那么就可以考虑添加一条正权边,满足这个条件就能添加dis1[A]+dis2[B]+1<=dis1[n],且cnt1[A]*cnt2[B]>=X,由于是要使方案增加,所以将边权设为dis1[n]-(dis1[A]+dis2[B])是最好的,因为可以继承原有的最短路方案数。那么由于(A,B)和(B,A)只算一次,那么第一维枚举到A第二维枚举到B,和第一维枚举到B第二维枚举到A会不会算同一种呢,可以证明这种情况并不会出现重复技计数。
那么考虑优化,枚举点A,点B需满足dis1[A]+dis2[B]+1<=dis1[n],那么可以将dis2进行排序,然后二分出临界范围,然后查询出这个范围内cnt2[B]>=X/cnt[A]的数目,离线的话做法估计挺多的,我代码里用了主席树,最后还需要去掉范围内已经连边的点和自身。时间复杂度O(nlogn)
代码
1 #include<cstdio> 2 #include<vector> 3 #include<set> 4 #include<queue> 5 #include<algorithm> 6 #define mp make_pair 7 #define pb push_back 8 #define fi first 9 #define sc second 10 using namespace std; 11 const int N = 201010; 12 const int M = 2010101; 13 const int inf = 2100000000; 14 typedef pair<int,int> P; 15 priority_queue<P,vector<P>,greater<P> > Q; 16 int n,m,i,a,b,c,dis[N],cnt[N],dis1[N],cnt1[N],dis2[N],cnt2[N],X; 17 int id[N],ID[N],Id[N]; 18 vector<P> e[N]; 19 void gao(int x) 20 { 21 int i; 22 for (i=1;i<=n;i++) 23 dis[i]=inf,cnt[i]=0; 24 dis[x]=0; 25 cnt[x]=1; 26 Q.push(mp(0,x)); 27 while (!Q.empty()) 28 { 29 P tmp=Q.top(); 30 Q.pop(); 31 x=tmp.sc; 32 if (dis[x]!=tmp.fi) continue; 33 for (i=0;i<e[x].size();i++) 34 if (dis[x]+e[x][i].sc<dis[e[x][i].fi]) 35 { 36 dis[e[x][i].fi]=dis[x]+e[x][i].sc; 37 cnt[e[x][i].fi]=cnt[x]; 38 Q.push(mp(dis[e[x][i].fi],e[x][i].fi)); 39 } 40 else 41 if (dis[x]+e[x][i].sc==dis[e[x][i].fi]) 42 { 43 cnt[e[x][i].fi]+=cnt[x]; 44 if (cnt[e[x][i].fi]>X) cnt[e[x][i].fi]=X; 45 } 46 } 47 } 48 49 int ls[M],rs[M],s[M],tot,root[N]; 50 void build(int &x,int a,int b) 51 { 52 x=++tot; 53 ls[x]=rs[x]=s[x]=0; 54 if (b-a>1) 55 { 56 int m=(a+b)>>1; 57 build(ls[x],a,m); 58 build(rs[x],m,b); 59 } 60 } 61 void insert(int y,int &x,int a,int b,int l,int r) 62 { 63 x=++tot; 64 ls[x]=ls[y];rs[x]=rs[y];s[x]=s[y]+1; 65 if ((a<=l)&&(r<=b)) 66 return; 67 int m=(l+r)>>1; 68 if (a<m) insert(ls[y],ls[x],a,b,l,m); 69 if (m<b) insert(rs[y],rs[x],a,b,m,r); 70 } 71 int query(int x,int a,int b,int l,int r) 72 { 73 if ((a<=l)&&(r<=b)) 74 return s[x]; 75 int m=(l+r)>>1,ans=0; 76 if (a<m) ans+=query(ls[x],a,b,l,m); 77 if (m<b) ans+=query(rs[x],a,b,m,r); 78 return ans; 79 } 80 bool cmp(int a,int b) 81 { 82 return dis2[a]<dis2[b]; 83 } 84 bool CMP(int a,int b) 85 { 86 return cnt2[a]>cnt2[b]; 87 } 88 int main() 89 { 90 while (~scanf("%d%d",&n,&m)) 91 { 92 if (n+m==0) return 0; 93 for (i=1;i<=n;i++) e[i].clear(); 94 scanf("%d",&X); 95 for (i=1;i<=m;i++) 96 { 97 scanf("%d%d%d",&a,&b,&c); 98 e[a].pb(mp(b,c)); 99 e[b].pb(mp(a,c)); 100 } 101 gao(1); 102 for (i=1;i<=n;i++) 103 dis1[i]=dis[i],cnt1[i]=cnt[i]; 104 gao(n); 105 for (i=1;i<=n;i++) 106 dis2[i]=dis[i],cnt2[i]=cnt[i]; 107 108 tot=0; 109 build(root[0],0,n); 110 for (i=1;i<=n;i++) 111 id[i]=i; 112 sort(id+1,id+1+n,cmp); 113 for (i=1;i<=n;i++) 114 ID[id[i]]=i; 115 116 for (i=1;i<=n;i++) 117 Id[i]=i; 118 sort(Id+1,Id+1+n,CMP); 119 for (i=1;i<=n;i++) 120 insert(root[i-1],root[i],ID[Id[i]]-1,ID[Id[i]],0,n); 121 long long ans=0; 122 for (i=1;i<=n;i++) 123 { 124 int l=1,r=n; 125 while (l<=r) 126 { 127 m=(l+r)>>1; 128 if (dis2[id[m]]+dis1[i]+1<=dis1[n]) l=m+1;else r=m-1; 129 } 130 int j=r; 131 132 l=1;r=n; 133 while (l<=r) 134 { 135 m=(l+r)>>1; 136 if (1LL*cnt1[i]*cnt2[Id[m]]>=X) l=m+1;else r=m-1; 137 } 138 139 if (j) ans=ans+query(root[r],0,j,0,n); 140 141 142 for (int k=0;k<e[i].size();k++) 143 if (dis2[e[i][k].fi]+1+dis1[i]<=dis1[n]) 144 if (1LL*cnt1[i]*cnt2[e[i][k].fi]>=X) ans--; 145 146 if (dis1[i]+dis2[i]+1<=dis1[N]) 147 if (1LL*cnt1[i]*cnt2[i]>=X) ans--; 148 149 } 150 printf("%lld\n",ans); 151 } 152 }