CF1322C
首先我们知道 $\gcd(a,a+b) = \gcd(a,b,a+b) = \gcd(a,b)$ ,这是显而易见的。
于是我们考虑对于两个右部点 $u,v$ ,假设与它们相邻的左部点集合是 $R(u),R(v)$ 这时它们的贡献。
- $R(u) = R(v)$ 这种时候,只有在计算 $N(S),S\subseteq R(u)$ 的时候会计算到 $w_u+w_v$,其他时候它们均不做贡献。也就是说,我们可以合并这样的点,所有这样的点都会一起被计算。
- $R(u) \subseteq R(v)$ 这种时候,当计算 $N(S),S\subseteq R(u)$ 的时候可以计算到 $w_u + w_v$ ,计算 $N(S),S\subseteq R(v),S \nsubseteq R(u)$ 时会计算 $w_v$ 但不会计算 $w_u$ ,所以最后做出贡献的时候可以直接拆开成 $w_u,w_v$
- 当 $R(u),R(v)$ 不存在包含关系,但是 $R(u)\cap R(v) \neq \varnothing$ ,这种情况,如果取到交集的子集,贡献是 $w_u+w_v$ ,否则取到某一边的贡献分别是 $w_u,w_v$ 就成为了开头那个 $\gcd(a,b,a+b)$ 的状况,仍然可以直接把 $u,v$ 的贡献看作 $w_u,w_v$
- $R(u) \cap R(v) = \varnothing$ 显然互不相干,分别是 $w_u,w_v$
所以对于所有右边的点,我们可以直接按照 $R(u)$ 分组,分组的过程用 hash 就行了。如果 $R(u),R(v)$ 不相同,总是可以把这两个数拆成 $w_u,w_v$ 直接进行贡献。
但是还要注意,如果右边某个点与左边根本不相连需要直接 skip 它。。因为这个场上吃了一发罚时。。
由于是 CF 建议 mt19937 + 双 hash。
可能有不严谨的地方。。但是可以意会一下吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include "iostream" #include "algorithm" #include "cstring" #include "cstdio" #include "vector" #include "chrono" #include "random" #include "map" using namespace std; #define ll long long #define MAXN 500006 mt19937 Rnd(chrono::high_resolution_clock::now().time_since_epoch().count()); int n , m; vector<int> G[MAXN]; const int P = 1000000007 , Q = 501217111; int bp , bq; int pp[MAXN] , pq[MAXN]; long long c[MAXN]; long long s[MAXN]; map<pair<int,int>,int> M; int cn = 0; long long gcd( long long a , long long b ) { return !b ? a : gcd( b , a % b ); } void solve( ) { scanf("%d%d",&n,&m); M.clear(); for( int i = 1 ; i <= n ; ++ i ) scanf("%lld",&c[i]) , s[i] = 0 , G[i].clear(); cn = 0; for( int i = 1 , u , v ; i <= m ; ++ i ) { scanf("%d%d",&u,&v); G[v].push_back( u ); } for( int i = 1 ; i <= n ; ++ i ) { int retp = 0 , retq = 0; if( !G[i].size() ) continue; for( int v : G[i] ) { ( retp += pp[v] ) %= P; ( retq += pq[v] ) %= Q; } pair<int,int> fk = make_pair( retp , retq ); if( !M[fk] ) M[fk] = ++ cn; s[M[fk]] += c[i]; } long long res = s[1]; for( int i = 2 ; i <= cn ; ++ i ) res = gcd( res , s[i] ); printf("%lld\n",res); } int main() { bp = Rnd() % 10007 + 1 , bq = Rnd() % 100007 + 1; pp[0] = pq[0] = 1; for( int i = 1 ; i < MAXN ; ++ i ) pp[i] = 1ll * pp[i - 1] * bp % P , pq[i] = 1ll * pq[i - 1] * bq % Q; int T;cin >> T; while( T-- ) { solve(); } }
|