题解 联合权值?改
考场上决策单调性+bitset水过的,但复杂度是假的,而且没开long long
正解没空写,先咕了
-
无向图三元环:由度数大的点向度数小的点连边
复杂度:令每条边对复杂度的贡献为 \(out_v\)(终点的出边个数),则
当 \(out_v > \sqrt m\) 时,因为 \(u\) 的度数要大于 \(v\) 的度数,所以 \(u\) 的个数是 \(\frac{n}{\sqrt m}\) 级别的,这部分复杂度为 \(n\sqrt m\)
当 \(out_v \leqslant \sqrt m\) 时,每条这样的边会带来 \(O(\sqrt m)\) 的复杂度,所以这部分是 \(O(m\sqrt m)\) 的
所以整体就是 \(O(m\sqrt m)\) 的Code(无向图三元环计数):
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define N 100010 #define ll long long #define pb push_back //#define int long long char buf[1<<21], *p1=buf, *p2=buf; #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++) inline int read() { int ans=0, f=1; char c=getchar(); while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();} while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();} return ans*f; } int n, m; int head[N], size, cnt[N], ans, tim[N]; vector<int> to[N]; struct edge{int to, next;}e[N<<2]; inline void add(int s, int t) {e[++size].to=t; e[size].next=head[s]; head[s]=size;} signed main() { memset(head, -1, sizeof(head)); n=read(); m=read(); for (int i=1,u,v; i<=m; ++i) { u=read(); v=read(); add(u, v); add(v, u); ++cnt[u]; ++cnt[v]; } for (int i=1; i<=n; ++i) { for (int j=head[i],v; ~j; j=e[j].next) { v = e[j].to; if (cnt[i]>cnt[v]) to[i].pb(v); else if (cnt[i]==cnt[v]&&i<v) to[i].pb(v); } } for (int i=1; i<=n; ++i) { for (auto v:to[i]) tim[v]=i; for (auto u:to[i]) { for (auto v:to[u]) { if (tim[v]==i) ++ans; } } } printf("%d\n", ans); return 0; }
-
在一个有 \(𝑚\) 条边的图中,三元环的个数为 \(𝑂(𝑚 \sqrt m)\) 的。显然一个点数为 \(O(\sqrt m)\) 的完全图可以使得三元环个数取到这个上界,或者考虑找三元环的过程也可以证明这个上界
所以在有些题里,爆扫所有三元环的复杂度可能是对的
好了不做大鸽子了,更正解
看完题解发现我第一问复杂度是对的只是我不会证
证明:对于枚举的每条出边,一旦形成一次匹配就会break,而不形成匹配一定是出现了三元环,而三元环只有 \(m\sqrt m\) 个
所以复杂度是 \(O(m\sqrt m)\) 的
至于第二问:
- 对于要求(图上)一些点权值的积/平方之类的,也许可以用到这样一个式子:\[(\sum a_i)^2 = \sum a_i^2 + \sum\limits_i\sum\limits_j a_ia_j[i\neq j] \]也即\[\sum\limits_i\sum\limits_j a_ia_j[i\neq j] = (\sum a_i)^2 - \sum a_i^2 \]常用于「求与一个点相连的所有点两两的权值的积的和」的 \(O(n^2)\) 化 \(O(n)\) 过程
应用到这个题上,就是先枚举 \(w\),求出所有与 \(w\) 相连的点对的权值积的和
然后三元环也会被统计进来,所以枚举三元环并减去其贡献即可
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 30010
#define ll long long
#define fir first
#define sec second
#define make make_pair
#define pb push_back
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m, t;
ll w[N];
vector<int> to[N];
pair<int, int> e[N];
bitset<30003> s[30003];
namespace force{
ll maxn=-1, sum;
void solve() {
for (int i=1; i<=m; ++i) s[e[i].fir][e[i].sec]=1, s[e[i].sec][e[i].fir]=1;
for (int i=1; i<=n; ++i) {
for (int j=0; j<to[i].size(); ++j) {
for (int k=0; k<j; ++k) {
int u=to[i][j], v=to[i][k];
if (!s[u][v]) {
sum+=w[u]*w[v];
maxn=max(maxn, w[u]*w[v]);
}
}
}
}
printf("%d\n", t!=2?maxn:0);
printf("%d\n", t!=1?sum*2:0);
exit(0);
}
}
namespace task1{
ll maxn=-1;
void solve() {
for (int i=1; i<=m; ++i) s[e[i].fir][e[i].sec]=1, s[e[i].sec][e[i].fir]=1;
for (int i=1; i<=n; ++i) {
for (int j=to[i].size()-1; j>=0; --j) {
for (int k=j-1; k>=0; --k) {
int u=to[i][j], v=to[i][k], tem=w[u]*w[v];
if (tem<=maxn) break;
if (!s[u][v]) {
maxn=max(maxn, tem);
}
}
}
}
printf("%d\n", maxn);
puts("0");
exit(0);
}
}
namespace task2{
ll sum=0;
void solve() {
for (int i=1; i<=m; ++i) s[e[i].fir][e[i].sec]=1, s[e[i].sec][e[i].fir]=1;
for (int i=1; i<=n; ++i) {
int tem=0, t2;
for (int j=to[i].size()-1; j>=0; --j) tem+=w[ to[i][j] ];
for (int j=to[i].size()-1; j>=0; --j) {
int u=to[i][j]; t2=tem-w[u];
for (int k:to[u]) if (s[i][k]) t2-=w[k];
sum+=w[u]*t2;
}
}
puts("0");
printf("%d\n", sum);
exit(0);
}
}
namespace task3{
ll maxn=-1, sum=0;
void solve() {
for (int i=1; i<=m; ++i) s[e[i].fir][e[i].sec]=1, s[e[i].sec][e[i].fir]=1;
for (int i=1; i<=n; ++i) {
for (int j=to[i].size()-1; j>=0; --j) {
for (int k=j-1; k>=0; --k) {
int u=to[i][j], v=to[i][k], tem=w[u]*w[v];
if (tem<=maxn) break;
if (!s[u][v]) {
maxn=max(maxn, tem);
}
}
}
}
for (int i=1; i<=n; ++i) {
int tem=0, t2;
for (int j=to[i].size()-1; j>=0; --j) tem+=w[ to[i][j] ];
for (int j=to[i].size()-1; j>=0; --j) {
int u=to[i][j]; t2=tem-w[u];
for (int k:to[u]) if (s[i][k]) t2-=w[k];
sum+=w[u]*t2;
}
}
printf("%d\n", t!=2?maxn:0);
printf("%d\n", t!=1?sum:0);
exit(0);
}
}
signed main()
{
freopen("link.in", "r", stdin);
freopen("link.out", "w", stdout);
// cout<<double(sizeof(s)+sizeof(to)+sizeof(e)+sizeof(w))/1024/1024<<endl;
n=read(); m=read(); t=read();
for (int i=1,u,v; i<=m; ++i) {
u=read(); v=read();
to[u].pb(v); to[v].pb(u);
e[i]=make(u, v);
}
for (int i=1; i<=n; ++i) w[i]=read();
for (int i=1; i<=n; ++i) sort(to[i].begin(), to[i].end(), [](int a, int b){return w[a]<w[b];});
if (n<=100) force::solve();
else if (t==1) task1::solve();
else if (t==2) task2::solve();
else task3::solve();
return 0;
}