题目链接
2023 Hubei Provincial Collegiate Programming Contest
Examples
input
6 6
1 3
2 3
1 4
2 5
3 6
4 6
output
30
input
6 4
1 2
3 5
2 4
3 6
output
0
题目大意:
给出结点个数 n n n和边的个数 m m m
下面依此给出 m m m个边,边是无向的,两端顶点为 u , v u,v u,v(注:允许自环)
定义deg i i i为顶点i的度
给出 f f f( u u u, v v v) = (deg u u u⊕deg v v v)(deg u u u | deg v v v)(deg u u u & deg v v v)
⊕ , | , & 分别是按位异或,按位或和按位且
求题目中的求和公式
题解
其实题目意思很简单,每个顶点和包括自生的其他顶点求一次 f f f然后相加,但是因为n有 1 0 6 10^6 106,如果挨个求就要O( n 2 n^2 n2)也就是O( 1 0 6 ∗ 1 0 6 10 ^6*10^6 106∗106)
就算剪枝掉度相等的两个结点(因为度相等,异或后结果为0,最后相乘也为0)和度为0的结点,也还是会TLE on 17,包括使用了离散化也是一样
那么我们可以反过来想,如果统计每一个度的结点数量,以这种角度去思考,那么相同度的结点就可以简化运算。包括使用离散化防止数据过于稀疏。
这里给出一个样例,如果是暴力算,就是第一个结点和其他结点去算,可以剪枝掉相同度的结点和度为0的结点,但这样有
n
n
n个结点就最多要算
n
(
n
−
1
)
/
2
n(n-1)/2
n(n−1)/2次
但如果是按照以度的角度去考虑
这个样例,度为1的有3个,度为2和度为3的结点各有1个,那么就可以将度为1和度为2的结点统一算,最后乘两个度结点和,
(
1
∣
2
)
(
1
2
)
(
1
⊕
2
)
∗
1
∗
3
(1|2)(1^2)(1⊕2)*1*3
(1∣2)(12)(1⊕2)∗1∗3,相当于当有
n
n
n各度的时候每多一种不同的度,就多算
(
n
−
1
)
(n-1)
(n−1)次即可。
那么根据图和规律可知,如果将
m
a
x
d
e
g
maxdeg
maxdeg记做能出现不同度的最大数量,将
m
a
x
j
i
e
maxjie
maxjie记作能出现不同结点的最大数量。那么显而易见
m
a
x
d
e
g
≤
m
a
x
j
i
e
maxdeg \leq maxjie
maxdeg≤maxjie,当然是远小于,因为要将度的种类+1,必须再多给
当前度的种类
当前度的种类
当前度的种类 个结点!
所以就算结点有
1
0
6
10^6
106个,最多就有
1413
1413
1413 个不同的度,时间复杂度一下子就降为
O
(
1413
∗
1413
)
O(1413*1413 )
O(1413∗1413)
包括使用离散化,收集稀疏的数据
C++/C
#include <iostream>
#define int long long
using namespace std;
const int N =1e6+10;
const int M=998244353;
int a[N];
int que[N];int que_i=0;
int deg[N];
signed main(void){
int n,m;
cin>>n>>m;
int ans=0;
while(m--){
int x,y;
scanf("%lld %lld",&x,&y);
a[x]++;
a[y]++;
}
for(int i=1;i<=n;i++){
if(!deg[a[i]])que[que_i++]=a[i];
deg[a[i]]++;
}
for(int i=0;i<que_i;i++){
for(int j=i+1;j<que_i;j++){
ans = (ans%M + ((que[j]^que[i])* (que[j]|que[i]) * (que[j]&que[i]) * deg[que[j]]*deg[que[i]])%M)%M;
}
}
//ans=(ans+(a[que[i]]^a[que[j]])*(a[que[i]]|a[que[j]])*(a[que[i]]&a[que[j]]))%M;
cout<<ans%M;
return 0;
}