基站建设
- problem
- solution
- code
problem
给定 nnn 个地点,以及每个地点的可靠度 RiR_iRi。
有 mmm 条光纤架,每一条连接两个不同的地点,且是双向的。
测试基站由 444 个信号塔,有 222 个主信号塔和 222 个副信号塔。
要求主信号塔必须与其余三个塔相连,副信号塔必须与所有主信号塔相连。
每个地点都只能建一个信号塔,基站的可靠度为两个主信号塔所在地点可靠度+1+1+1的乘积再加上两个副信号塔所在地可靠度的乘积。即 (Ri+1)(Rj+1)+RaRb(R_i+1)(R_j+1)+R_aR_b(Ri+1)(Rj+1)+RaRb。
求测试基站的最大可靠度。
n≤3e5,1s,512MBn\le 3e5,1s,512MBn≤3e5,1s,512MB。
solution
observation
:满足条件的子图其实上是有一条公共边的两个三元环。
求解公式并没有什么特殊性质,所以很有可能是需要枚举三元环的。
枚举三元环既可以枚举三个点,也可以枚举三条边,选择不同带来的时间效率也有所不同。
考虑根号分治。
对于度数 <m<\sqrt{m}<m 的点,枚举其两条出边再判断两条边的入点之间是否有边。
度数 >m>\sqrt{m}>m 的点,总个数不超过 m\sqrt{m}m 个。直接枚举三个点。
时间复杂度均为 O(mm)O(m\sqrt{m})O(mm)。
上面的是正解,下面的是我在考场上自己想的做法。
最暴力的就是考虑一条边,这条边是连接主信号塔 u,vu,vu,v 之间的。
其实剩下的就是找 u,vu,vu,v 连接的点的交集之中可靠度最大的两个点。
暴力枚举边就是 O(m2)O(m^2)O(m2) 的。但是一旦使用 bitset
就不一样了。
直接 bitset
处理每个点直接相连的点的集合,将 u,vu,vu,v 的 bitset
直接取 &
。
然后 bitset
也自带快速找到为 111 的位置。
空间复杂度 O(n2)O(n^2)O(n2)。不卡!
时间复杂度是 O(nmω)O(\frac{nm}{\omega})O(ωnm)
反正跑得挺快的。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 50005
#define maxm 200005
bitset < maxn > g[maxn];
pair < int, int > E[maxm];
int R[maxn];
int n, m;int main() {freopen( "station.in", "r", stdin );freopen( "station.out", "w", stdout );scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ ) scanf( "%d", &R[i] );for( int i = 1, u, v;i <= m;i ++ ) {scanf( "%d %d", &u, &v );E[i] = { u, v };g[u][v] = g[v][u] = 1;}int ans = 0;for( int i = 1;i <= m;i ++ ) {int u = E[i].first, v = E[i].second;bitset < maxn > son = g[u] & g[v];son[u] = son[v] = 0;int max1 = 0, max2 = 0;for( int k = son._Find_first();k != son.size();k = son._Find_next( k ) ) {if( R[k] > max1 ) max2 = max1, max1 = R[k];else if( R[k] > max2 ) max2 = R[k];}if( ! max1 or ! max2 ) continue;ans = max( ans, ( R[u] + 1 ) * ( R[v] + 1 ) + max1 * max2 );}printf( "%d\n", ans );return 0;
}