Liveddd's Blog

愛にできることはまだあるかい

P8907 [USACO22DEC] Making Friends P

线段树合并,也可以启发式合并。

首先,对于一条边 $i \to j$,只有 $i < j$ 时有意义,并且。对于每个点,统计时我们也只统计这样的边。考虑维护每个点与相邻点的点集,而每次将该点的点集合并到 该点的点集中编号最小的点上,仔细思考会发现这样是不重不漏的。

较为简单地可以用 set 启发式合并维护这个点集,时间复杂度 $\mathcal O(n\log^2 n)$。但更加优秀的是用线段树合并来维护,时间复杂度 $\mathcal O(n \log n)$,但是竟然没有前面一种写法跑得快

Code
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const int N=2e5+10;
template<class T>
inline void read(T &x)
{
x=0;int f=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(f)x=~x+1;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1)
{
read(x),read(x1...);
}
int n,m;
ll ans;
int tot,rt[N];
struct Node
{
int lc,rc;
int sum;
}tr[N*80];
inline void pushup(int x)
{
tr[x].sum=tr[tr[x].lc].sum+tr[tr[x].rc].sum;
}
void insert(int &x,int l,int r,int pos)
{
if(!x)x=++tot;
if(l==r)return tr[x].sum=1,void();
int mid=l+r>>1;
if(pos<=mid)insert(tr[x].lc,l,mid,pos);
else insert(tr[x].rc,mid+1,r,pos);
pushup(x);
}
int merge(int p,int q,int l,int r)
{
if(!p||!q)return p+q;
if(l==r)
{
tr[p].sum=tr[p].sum|tr[q].sum;
return p;
}
int mid=l+r>>1;
tr[p].lc=merge(tr[p].lc,tr[q].lc,l,mid);
tr[p].rc=merge(tr[p].rc,tr[q].rc,mid+1,r);
pushup(p);
return p;
}
int query(int x,int l,int r)
{
if(l==r)return l;
int mid=l+r>>1;
if(tr[x].lc&&tr[tr[x].lc].sum)return query(tr[x].lc,l,mid);
return query(tr[x].rc,mid+1,r);
}
void modify(int x,int l,int r,int pos,int k)
{
if(l==r)return tr[x].sum=k,void();
int mid=l+r>>1;
if(pos<=mid)modify(tr[x].lc,l,mid,pos,k);
else modify(tr[x].rc,mid+1,r,pos,k);
pushup(x);
}
int main()
{
read(n,m);
for(int i=1;i<=m;i++)
{
int u,v;
read(u,v);
if(u>v)swap(u,v);
insert(rt[u],1,n,v);
}
for(int x=1;x<=n;x++)
{
if(!tr[rt[x]].sum)continue;
ans+=tr[rt[x]].sum;
int y=query(rt[x],1,n);
modify(rt[x],1,n,y,0);
rt[y]=merge(rt[y],rt[x],1,n);
}
printf("%lld\n",ans-m);
return 0;
}