CF1322C

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();
}
}
文章作者: yijan
文章链接: https://yijan.co/cf1322c/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog