【四校联考】点
【题目描述】
有n个点,初始时没有边。有m个操作,操作分为两种:
(1) 在i和j之间增加一条无向边,保证1<=i,j<=n。
(2) 删去最后添加的k条边,保证k<=当前边数。
你想要知道最多能选取多少个两两不连通的点,以及选取的方案数。在每次操作后输出这两个值。方案数对998244353取模。
【输入数据】
第一行两个整数n,m。接下来m行每行第一个数表示操作类型,接下来2或1个数表示i,j或k。
【输出数据】
对于每个操作,输出一行两个整数,用一个空格隔开。
【样例输入】
3 7
1 1 2
1 1 3
1 3 3
2 1
1 1 2
2 2
2 1
【样例输出】
2 2
1 3
1 3
1 3
1 3
2 2
3 1
【数据范围】
对于20%的数据,n,m<=10。
对于40%的数据,n,m<=1000。
对于100%的数据,n,m<=500000。
【题解】
这是一个维护连通性的问题
我们发现每次都只会删除最近的边
于是我们把每次加的边压入栈中
一条边只会弹出一次
于是我们采用启发式合并维护
1 #include<stdio.h> 2 #include<algorithm> 3 #define mod 998244353 4 using namespace std;const int N=500001;typedef long long ll; 5 int n,m,top,p[N],q[N],f[N],s[N],ans1,ans2,r[N]; 6 inline int getf(int h){return f[h]?getf(f[h]):h;} 7 inline void join(int x,int y){ 8 (s[x]>s[y])?(swap(x,y),0):0;f[x]=y;ans1--;s[y]+=s[x];p[top]=x;q[top]=y; 9 ans2=1ll*ans2*r[s[y]-s[x]]%mod*r[s[x]]%mod*s[y]%mod; 10 } 11 inline void split(){ 12 f[p[top]]=0;ans1++;s[q[top]]-=s[p[top]]; 13 ans2=1ll*ans2*r[s[q[top]]+s[p[top]]]%mod*s[p[top]]%mod*s[q[top]]%mod; 14 } 15 int main(){ 16 freopen("point.in","r",stdin);freopen("point.out","w",stdout); 17 scanf("%d%d",&n,&m);ans1=n;ans2=1;r[1]=s[1]=1; 18 for(int i=2;i<=n;i++){s[i]=1;r[i]=(-1ll*(mod/i)*r[mod%i]%mod+mod)%mod;} 19 for(int py=1,fx,fy,t,x,y;py<=m;py++){ 20 scanf("%d%d",&t,&x); 21 if(t==1){ 22 scanf("%d",&y);fx=getf(x);fy=getf(y);++top; 23 (fx!=fy)?(join(fx,fy),1):(p[top]=q[top]=0); 24 } 25 else for(int jy=1;jy<=x;jy++,top--) if(p[top]) split(); 26 printf("%d %d\n",ans1,ans2); 27 } 28 return 0; 29 }
蜉蝣渴望着飞翔,尽管黄昏将至