第十二届湖南省赛 (B - 有向无环图 )(拓扑排序+思维)好题
Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 v 开始、点 v 结束的路径)。
为了方便,点用 1,2,…,n 编号。 设 count(x,y) 表示点 x 到点 y 不同的路径数量(规定 count(x,x)=0),Bobo 想知道
除以 (10 9+7) 的余数。
其中,a i,b j 是给定的数列。
Input
输入包含不超过 15 组数据。
每组数据的第一行包含两个整数 n,m (1≤n,m≤10 5).
接下来 n 行的第 i 行包含两个整数 a i,b i (0≤a i,b i≤10 9).
最后 m 行的第 i 行包含两个整数 u i,v i,代表一条从点 u i 到 v i 的边 (1≤u i,vi≤n)。
Output对于每组数据,输出一个整数表示要求的值。Sample Input
3 3 1 1 1 1 1 1 1 2 1 3 2 3 2 2 1 0 0 2 1 2 1 2 2 1 500000000 0 0 500000000 1 2
Sample Output
4 4 250000014
Hint
思路:
由有向图拓扑序的性质可以知道,拓扑序在后的节点是没有指向拓扑序在前的节点。
那么我们在对整个图进行拓扑的时候,把起始点 x 的a[x] 值 附加到 这个边的终点 y 的a[y] 值上。
并且对于每一个边,我们维护一个 long long 类型的 答案 ans,ans+= a[x]*b[y] ( 边 是 x ~> y )
那么这里就介绍刚刚我们为什么要附加数值a[x] 到a[y]上。
如果由三个点 x y z ,由如下的连接关系 x ~> y ~> z 那么x到y的边我们会算一次对答案的贡献值,y到z的边我们也会算一次。
还有一个x到z的边,我们仍然需要算。因为x可以到达z,那么如果我们在拓扑的时候把a[y]加上a[x] 时, 我们在算 b到z 的边的时候就顺便的加上了a到z的边。
因为x 的拓扑优先级比y高,并且x可以到达y,那么x就可以到达y能到达的所有边,那么就解释了这样做的原因。
这样做的时间复杂度就会转化为 O ( N )
主要是理解后就很好写代码,可以自己动手画图理解一下上面 讲的内容。
实现细节见ac代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #include <vector> #include <iomanip> #define ALL(x) (x).begin(), (x).end() #define rt return #define dll(x) scanf("%I64d",&x) #define xll(x) printf("%I64d\n",x) #define sz(a) int(a.size()) #define all(a) a.begin(), a.end() #define rep(i,x,n) for(int i=x;i<n;i++) #define repd(i,x,n) for(int i=x;i<=n;i++) #define pii pair<int,int> #define pll pair<long long ,long long> #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define MS0(X) memset((X), 0, sizeof((X))) #define MSC0(X) memset((X), '\0', sizeof((X))) #define pb push_back #define mp make_pair #define fi first #define se second #define eps 1e-6 #define gg(x) getInt(&x) #define db(x) cout<<"== [ "<<x<<" ] =="<<endl; using namespace std; typedef long long ll; ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} ll lcm(ll a,ll b){return a/gcd(a,b)*b;} ll powmod(ll a,ll b,ll MOD){ll ans=1;while(b){if(b%2)ans=ans*a%MOD;a=a*a%MOD;b/=2;}return ans;} inline void getInt(int* p); const int maxn=100010; const int inf=0x3f3f3f3f; /*** TEMPLATE CODE * * STARTS HERE ***/ std::vector<int> son[maxn]; const ll mod=1e9+7ll; ll ans=0ll; ll a[maxn]; ll b[maxn]; ll num[maxn]; int du[maxn]; int main() { // freopen("D:\\common_text\\code_stream\\in.txt","r",stdin); //freopen("D:\\common_text\\code_stream\\out.txt","w",stdout); gbtb; int n,m; while(cin>>n>>m) { repd(i,1,n) { son[i].clear(); } MS0(du); MS0(num); repd(i,1,n) { cin>>a[i]>>b[i]; } int x,y; repd(i,1,m) { cin>>x>>y; son[x].push_back(y); du[y]++; } queue<int> q; repd(i,1,n) { if(!du[i]) { q.push(i); } } ans=0ll; while(q.size()) { int temp=q.front(); q.pop(); for(auto x:son[temp]) { ans=(ans+(a[temp]*b[x])%mod+mod)%mod; a[x]+=a[temp]; a[x]=(a[x]+mod)%mod; du[x]--; if(!du[x]) { q.push(x); } } } cout<<ans<<endl; } return 0; } inline void getInt(int* p) { char ch; do { ch = getchar(); } while (ch == ' ' || ch == '\n'); if (ch == '-') { *p = -(getchar() - '0'); while ((ch = getchar()) >= '0' && ch <= '9') { *p = *p * 10 - ch + '0'; } } else { *p = ch - '0'; while ((ch = getchar()) >= '0' && ch <= '9') { *p = *p * 10 + ch - '0'; } } }
本博客为本人原创,如需转载,请必须声明博客的源地址。
本人博客地址为:www.cnblogs.com/qieqiemin/
希望所写的文章对您有帮助。