Problem
Input
Output
对于每个Q操作,输出一行个整数为相应时刻的分子。
Hint
Solution
一眼看过去,不就是弦图吗没思路。再看一下数据范围,顿时感觉有点希望。
由于我不会弦图,所以我在这里提供一种题目不需保证给出的图是弦图也能过的方法。
首先,考虑对于联通块a和b来说,若有x,y两边将它们连接,且x比y早报销,不考虑联通块以内的情况,x是没有建的必要的。
所以我们将会报销的第i条边的权值设定为
q−ti
q
−
t
i
,q为操作数,
ti
t
i
为第i条边报销的时间。不直接设为
−ti
−
t
i
是因为如果它不会报销,它就不会被设定权值,权值即为0;如果会报销,权值则会≥0。
我们首先将初始的m条边排个序,然后做一遍kruskal,加上并查集优化。设此时最小生成树中的k条边为
a1,a2,...,ak
a
1
,
a
2
,
.
.
.
,
a
k
。
然后对于连边操作,我们就把新的那条边插入排序到a数组中,然后再做一遍kruskal。
对于删边操作,我们把删的那条边在最小生成树中找一遍,如果找到了,我们就删掉它,然后再做一遍kruskal。
对于询问,我们可以扫一遍所有点,算出它们在在并查集中的getfat,记录下不同的getfat个数,即为答案。
但是还可以优化。
对于连边操作,如果因此而多选了一条边,那么联通块的个数肯定–;对于删边操作,如果在最小生成树中删掉它了,那么联通块的个数肯定++。
所以,我们只需要一开始先求出一个cnt,每次操作就–、++或不变,这样就可以达成询问
O(1)
O
(
1
)
。
时间复杂度:
O(mlog2m+nqα(n))
O
(
m
l
o
g
2
m
+
n
q
α
(
n
)
)
。
补充一句:这样做很诡异地慢,所以请无视掉下发代码中的O3。
Code
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define M 200001
#define Q 10001
#define S M+Q
#define N 5001
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
int i,j,l,n,m,q,x[Q],y[Q],s,map[N][N],t,k,fat[N],cnt,bz[N],next[S],last[N][N],lk;
struct side
{
int a,b,t,num;
}a[M],b[Q];
char ch[Q];
__attribute__((optimize("-O3")))
inline bool operator<(const side&a,const side&b)
{
return a.t<b.t;
}
__attribute__((optimize("-O3")))
int gef(int x)
{
return x==fat[x]?x:fat[x]=gef(fat[x]);
}
__attribute__((optimize("-O3")))
void kruskal(int m)
{
int i,fx,fy;
k=0;
fo(i,1,n)fat[i]=i;
fo(i,1,m)
{
fx=gef(a[i].a);
fy=gef(a[i].b);
if(fx!=fy)
{
fat[fx]=fy;
a[++k]=a[i];
if(k==n-1)break;
}
}
}
__attribute__((optimize("-O3")))
int main()
{
freopen("compound.in","r",stdin);
freopen("compound.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,m)
{
scanf("%d%d",&a[i].a,&a[i].b);
if(a[i].a>a[i].b)swap(a[i].a,a[i].b);
last[a[i].a][a[i].b]=map[a[i].a][a[i].b]=i;
a[i].num=i;
}
scanf("%d",&q);
fo(i,1,q)
{
do
scanf("%c",&ch[i]);
while(ch[i]=='\n');
if(ch[i]=='Q')continue;
scanf("%d%d",&x[i],&y[i]);
if(x[i]>y[i])swap(x[i],y[i]);
if(ch[i]=='A')
{
s++;
if(!map[x[i]][y[i]])
map[x[i]][y[i]]=s+m;
else next[map[x[i]][y[i]]]=s+m;
last[x[i]][y[i]]=s+m;
b[s].a=x[i];
b[s].b=y[i];
b[s].num=s+m;
continue;
}
t=last[x[i]][y[i]];
if(t<=m)
a[t].t=q-i;
else b[t-m].t=q-i;
}
sort(a+1,a+m+1);
kruskal(m);
fo(j,1,n)
if(bz[gef(j)]<i)
cnt++,bz[fat[j]]=i;
fo(i,1,q)
switch(ch[i])
{
case 'A':
{
t=map[x[i]][y[i]];
a[k+1]=b[t-m];
fd(j,k,1)
if(a[j+1].t<a[j].t)
swap(a[j+1],a[j]);
else break;
lk=k;
kruskal(k+1);
cnt+=lk-k;
break;
}
case 'D':
{
t=map[x[i]][y[i]];
fo(j,1,k)
if(a[j].num==t)
{
fo(l,j,k)a[l]=a[l+1];
kruskal(--k);
cnt++;
break;
}
map[x[i]][y[i]]=next[t];
break;
}
case 'Q':
{
printf("%d\n",cnt);
break;
}
}
}