这次又被抓过去写noip2021了\(qaq\)
P7960 [NOIP2021] 报数
可以用类似于质数筛的方法筛一遍,做到 \(\mathcal O(\)值域\()\) 的预处理,以及 \(\mathcal O(1)\) 的查询。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mxn 10000010
#define mxm 200010
bool vis[mxn];
int nxt[mxn];
inline bool check(int x){while(x){if(x%10==7)return 1;x/=10;}return 0;
}
void init(){for(int i=1;i<=mxn-10;i++){if(vis[i])continue;if(check(i))for(ll j=1;j*i<=mxn-10;j++)vis[j*i]=1;}ll lst=10000001;for(int i=mxn-10;i;i--)if(!vis[i])nxt[i]=lst,lst=i;
}
void solve(){int x;cin>>x;if(vis[x])cout<<"-1\n";else cout<<nxt[x]<<'\n';
}
int main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);init();int T;cin>>T;while(T--)solve();return 0;
}
P7961 [NOIP2021] 数列
这题数据小,考虑使用超大力dp。
设 \(dp[i][j][k][l]\) 为讨论到 \(S\) 的第 \(i\) 位(二进制下),确定了 \(a\) 序列前 \(k\) 个数,\(S\) 前面有 \(k\) 个 \(1\),且从上一位进位过来的有 \(l\) 位,假设序列上有 \(o\) 个 \(i\),则有方程:
暴力转移即可。
#include<bits/stdc++.h>
using namespace std;
#define mxn 40
#define mxm 110
#define ll long long
#define mod 998244353
#define bpop __builtin_popcount
ll n,m,k,c[mxn][mxn],v[mxm];
ll poww[mxm][mxm],dp[mxm][mxn][mxn][mxm];
ll ans;
void init(){c[0][0]=1;for(int i=1;i<=n;i++){c[i][0]=1;for(int j=1;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;}for(int i=0;i<=m;i++){poww[i][0]=1;for(int j=1;j<=n;j++)poww[i][j]=poww[i][j-1]*v[i]%mod;}
}
int main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>n>>m>>k;for(int i=0;i<=m;i++)cin>>v[i];init();//i:讨论到第i位//j:确定了前j个数//k:前面有k个1//l:从上一位进位l个//o:给S贡献o个1dp[0][0][0][0]=1;for(int i=0;i<=m;i++)for(int j=0;j<=n;j++)for(int kk=0;kk<=k;kk++)for(int l=0;l<=n/2;l++)for(int o=0;o<=n-j;o++)dp[i+1][j+o][kk+((l+o)&1)][(l+o)/2]=( dp[i+1][j+o][kk+((l+o)&1)][(l+o)/2]+dp[i][j][kk][l]*poww[i][o]%mod*c[n-j][o]%mod )%mod;for(int i=0;i<=k;i++)for(int j=0;j<=n/2;j++)if(i+bpop(j)<=k)ans=(ans+dp[m+1][n][i][j])%mod;cout<<ans;
}
P7962 [NOIP2021] 方差
什么 \(dp\)?没听说过啊。
考虑到差分数组为单谷函数时方差最小,直接随机化shuffle
差分数组搞定!
#include<bits/stdc++.h>
#define ll long long
#define inf 1e15
#define mxn 10100
using namespace std;
ll a[mxn],b[mxn],n;
int main(){srand(unsigned(time(0)));ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>n; int time=clock(); for(int i=1;i<=n;i++)cin>>a[i];for(int i=1;i<=n;i++)b[i]=a[i]-a[i-1];sort(b+2,b+n+1);ll suf=0,sum=0,ans=inf;int mid=(n+1)/2;while(1){suf=0,sum=0;random_shuffle(b+2,b+n+1);sort(b+2,b+mid,[](int x,int y){return x>y;}),sort(b+mid+1,b+n+1);for(int i=1;i<=n;i++)a[i]=a[i-1]+b[i];for(int i=1;i<=n;i++)suf+=n*a[i]*a[i],sum+=a[i];ans=min(ans,suf-sum*sum);if(clock()-time>=990000)break;}cout<<ans;return 0;
}
P7963 [NOIP2021] 棋局
极其恶心的数据结构题。
首先,对于 \(\rm case\ 1\sim8\),可以直接暴力。
而对于 \(\rm case\ 9\sim14\),可以写并查集维护。
接下来来到正解环节。
由于棋子是一个一个加入的,所以连通块是不断分割的,有点难以维护,不妨倒着处理。
我们先写一个并查集,用于维护互通道路组成的连通块,以及直行道路组成的连通块(这里横向和纵向分开处理,原因后面讲)。
这个并查集需要维护连通块的大小,以及块内的编号最大最小值。
struct Dsu{int f[mxn],sze[mxn],minn[mxn],maxn[mxn];int fu,fv;I void init(){for(int i=1;i<=n*m;i++)f[i]=minn[i]=maxn[i]=i,sze[i]=1;}I int fnd(int x){return x==f[x]?x:f[x]=fnd(f[x]);}I void merge(int u,int v,int opt){fu=fnd(u),fv=fnd(v);if(fu==fv)return;if(sze[fu]>sze[fv])swap(fu,fv);f[fu]=fv,sze[fv]+=sze[fu];maxn[fv]=maxn[fu]>maxn[fv]?maxn[fu]:maxn[fv];minn[fv]=minn[fu]<minn[fv]?minn[fu]:minn[fv];if(opt)Merge(fu,fv);}
}dsu[3];
那吃子怎么办呢?
如果是走互通道路,则对于每一个联通块,可以开一个线段树(当然要动态开点),把连通块周围能吃掉的棋子存进去(要分两个颜色维护),若是黑色棋子,查询能吃到的白色棋子的数量即可。
如果是普通或直行道路,那最多吃四个子,直接判断就行了。
当合并连通块时,可以将两个联通块的线段树也合并进去。
这里可以按照加入棋子的顺序离散化每个棋子的等级,使每个棋子的等级不同。
//就像这样
struct Chess{int lvl,col,x,y,id;
}ch[mxn],ch2[mxn];
void solve(){sort(ch2+1,ch2+q+1,[](Chess a,Chess b){return a.lvl==b.lvl?a.id<b.id:a.lvl<b.lvl;});for(int i=1;i<=q;i++)ch[ch2[i].id]=ch2[i],ch[ch2[i].id].lvl=i;
}
线段树:
struct Seg{int ls[mxm],rs[mxm],cnt,rt[mxn<<1],sum[mxm];I void init(){memset(rt,0,sizeof(rt));for(int i=1;i<=cnt;i++)ls[i]=rs[i]=sum[i]=0;cnt=0;}I void push_up(int rot){sum[rot]=sum[ls[rot]]+sum[rs[rot]];}I int insert(int rot,int l,int r,int x){//单点插入if(!rot)rot=++cnt;//动态开店if(l==r){if(!sum[rot])sum[rot]++;return rot;}int mid=(l+r)>>1;if(mid>=x)ls[rot]=insert(ls[rot],l,mid,x);else rs[rot]=insert(rs[rot],mid+1,r,x);push_up(rot);return sum[rot]?rot:0;}I int remove(int rot,int l,int r,int x){//单点删除if(!rot)rot=++cnt;if(l==r)return 0;int mid=(l+r)>>1;if(mid>=x)ls[rot]=remove(ls[rot],l,mid,x);else rs[rot]=remove(rs[rot],mid+1,r,x);push_up(rot);return sum[rot]?rot:0;}I int merge(int rot1,int rot2,int l,int r){//线段树合并if(!rot1||!sum[rot1])return rot2;if(!rot2||!sum[rot2])return rot1;if(l==r){sum[rot2]=min(sum[rot1]+sum[rot2],1);return rot2;}int mid=(l+r)>>1;ls[rot2]=merge(ls[rot1],ls[rot2],l,mid);rs[rot2]=merge(rs[rot1],rs[rot2],mid+1,r);push_up(rot2);return rot2;}I int query(int rot,int l,int r,int x,int y,int opt=0){//区间求和if(!rot||y<l||x>r)return 0;if(l>=x&&r<=y)return sum[rot];int mid=(l+r)>>1,ret=0;if(x<=mid)ret+=query(ls[rot],l,mid,x,y,opt);if(y>mid)ret+=query(rs[rot],mid+1,r,x,y,opt);return ret;}
}Scol[2];
但是这样还不够,因为有可能一个点可以同时从直行道路和互通道路走到。所以要去重。
考虑同一行/列的点是连续的,所以可以按两种方式编号(就是之前讲的横向纵向分开处理),再插到线段树里,去重的时候直接区间查询就行了。
首先是预处理环节:
for(int i=1;i<=q;i++)vis[get1(ch[i].x,ch[i].y)]=i;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int id=get1(i,j),nxt;if(vis[id])continue;//如果这个点上有棋子直接跳过for(int k=1;k<=4;k++){int fx=i+dir[k][0],fy=j+dir[k][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);if(vis[nxt])continue;if(edge[id][k]==2)dsu[k%2+1].merge(id,nxt,0);//如果为直行道路,分行/列放到并查集里if(edge[id][k]==3)dsu[0].merge(id,nxt,0);//如果为互通道路}}
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int id1=get1(i,j),id2=get2(i,j),id=dsu[0].fnd(id1),nxt,col;//按两种方式编号Sdir[0].rt[id]=Sdir[0].insert(Sdir[0].rt[id],1,n*m,id1);//分别插进两个线段树里Sdir[1].rt[id]=Sdir[1].insert(Sdir[1].rt[id],1,n*m,id2);for(int k=1;k<=4;k++){int fx=i+dir[k][0],fy=j+dir[k][1];if(edge[id1][k]!=3||fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);//如果是互通道路if(!vis[nxt])continue;col=ch[vis[nxt]].col;//按颜色,将每个棋子的等级插进去Scol[col].rt[id]=Scol[col].insert(Scol[col].rt[id],1,q,ch[vis[nxt]].lvl);}}
接下来,首先是计算互通道路的贡献:
for(int j=1;j<=4;j++){if(edge[id][j]!=3)continue;fx=X+dir[j][0],fy=Y+dir[j][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=dsu[0].fnd(get1(fx,fy));Scol[col].remove(Scol[col].rt[nxt],1,q,ch[i].lvl);//之后这个棋子就不在了,不能被吃掉,所以要移除
}
for(int j=1;j<=4;j++){if(edge[id][j]!=3)continue;fx=X+dir[j][0],fy=Y+dir[j][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);if(vis[nxt]&&vis[nxt]<i)continue;dsu[0].merge(id,nxt,1);//将这个棋子合并进周围的连通块里
}
fid1=dsu[0].fnd(id);
ans[i]+=dsu[0].sze[fid1]-1;//-1是因为去掉棋子所在点的贡献
ans[i]+=Scol[!col].query(Scol[!col].rt[fid1],1,q,1,ch[i].lvl,i==4?1:0);//能吃的棋子数
然后是直行道路:
for(int j=1;j<=4;j++){if(edge[id][j]!=2)continue;fx=X+dir[j][0],fy=Y+dir[j][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);if(vis[nxt]&&vis[nxt]<i)continue;dsu[j%2+1].merge(id,nxt,0);//横向/纵向的合并
}
fid2=dsu[2].fnd(id);
int maxn,minn;
maxn=dsu[2].maxn[fid2],minn=dsu[2].minn[fid2];
ans[i]+=maxn-minn+1;//先算横向的贡献
ans[i]-=Sdir[0].query(Sdir[0].rt[fid1],1,n*m,minn,maxn);//去重,将minn~maxn范围内互通道路能走到的点的数量去掉
if(edge[minn][1]==2&&eat(i,vis[minn-1])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[minn-1]].lvl,ch[vis[minn-1]].lvl)==0)ans[i]++;
if(edge[maxn][3]==2&&eat(i,vis[maxn+1])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[maxn+1]].lvl,ch[vis[maxn+1]].lvl)==0)ans[i]++;
//计算吃子数,注意这里计算时要看这个子能不能从互通道路走过去吃掉
fid2=dsu[1].fnd(id);
maxn=dsu[1].maxn[fid2],minn=dsu[1].minn[fid2];
ans[i]+=(maxn-minn)/m+1;//纵向同理
if(edge[minn][2]==2&&eat(i,vis[minn-m])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[minn-m]].lvl,ch[vis[minn-m]].lvl)==0)ans[i]++;
if(edge[maxn][4]==2&&eat(i,vis[maxn+m])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[maxn+m]].lvl,ch[vis[maxn+m]].lvl)==0)ans[i]++;
fx=getx(maxn),fy=gety(maxn),maxn=get2(fx,fy);
fx=getx(minn),fy=gety(minn),minn=get2(fx,fy);
ans[i]-=Sdir[1].query(Sdir[1].rt[fid1],1,n*m,minn,maxn);
最后是普通道路:
for(int j=1;j<=4;j++){if(edge[id][j]!=1)continue;fx=X+dir[j][0],fy=Y+dir[j][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);if(vis[nxt]&&vis[nxt]<i){if(eat(i,vis[nxt])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[nxt]].lvl,ch[vis[nxt]].lvl)==0)ans[i]++;}//这里计算时要看这个子能不能从互通道路走过去吃掉else if(dsu[0].fnd(id)!=dsu[0].fnd(nxt))ans[i]++;//如果是在一个块里,说明互通道路可以走到,不计算
}
最后的最后,献上完整代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mxn 200010
#define mxm 5000010
#define I inline
int n,m,q,edge[mxn][5],vis[mxn],ans[mxn];
int dir[5][2]={{0,0},{0,-1},{-1,0},{0,1},{1,0}};
char str[mxn];
I int get1(int x,int y){return (x-1)*m+y;}
I int get2(int x,int y){return (y-1)*n+x;}
I int getx(int x){return (x-1)/m+1;}
I int gety(int x){return (x-1)%m+1;}
I void Merge(int u,int v);
struct Chess{int lvl,col,x,y,id;
}ch[mxn],ch2[mxn];
struct Dsu{int f[mxn],sze[mxn],minn[mxn],maxn[mxn];int fu,fv;I void init(){for(int i=1;i<=n*m;i++)f[i]=minn[i]=maxn[i]=i,sze[i]=1;}I int fnd(int x){return x==f[x]?x:f[x]=fnd(f[x]);}I void merge(int u,int v,int opt){fu=fnd(u),fv=fnd(v);if(fu==fv)return;if(sze[fu]>sze[fv])swap(fu,fv);f[fu]=fv,sze[fv]+=sze[fu];maxn[fv]=maxn[fu]>maxn[fv]?maxn[fu]:maxn[fv];minn[fv]=minn[fu]<minn[fv]?minn[fu]:minn[fv];if(opt)Merge(fu,fv);}
}dsu[3];
struct Seg{int ls[mxm],rs[mxm],cnt,rt[mxn<<1],sum[mxm];I void init(){memset(rt,0,sizeof(rt));for(int i=1;i<=cnt;i++)ls[i]=rs[i]=sum[i]=0;cnt=0;}I void push_up(int rot){sum[rot]=sum[ls[rot]]+sum[rs[rot]];}I int insert(int rot,int l,int r,int x){if(!rot)rot=++cnt;if(l==r){if(!sum[rot])sum[rot]++;return rot;}int mid=(l+r)>>1;if(mid>=x)ls[rot]=insert(ls[rot],l,mid,x);else rs[rot]=insert(rs[rot],mid+1,r,x);push_up(rot);return sum[rot]?rot:0;}I int remove(int rot,int l,int r,int x){if(!rot)rot=++cnt;if(l==r)return 0;int mid=(l+r)>>1;if(mid>=x)ls[rot]=remove(ls[rot],l,mid,x);else rs[rot]=remove(rs[rot],mid+1,r,x);push_up(rot);return sum[rot]?rot:0;}I int merge(int rot1,int rot2,int l,int r){if(!rot1||!sum[rot1])return rot2;if(!rot2||!sum[rot2])return rot1;if(l==r){sum[rot2]=min(sum[rot1]+sum[rot2],1);return rot2;}int mid=(l+r)>>1;ls[rot2]=merge(ls[rot1],ls[rot2],l,mid);rs[rot2]=merge(rs[rot1],rs[rot2],mid+1,r);push_up(rot2);return rot2;}I int query(int rot,int l,int r,int x,int y,int opt=0){if(!rot||y<l||x>r)return 0;if(l>=x&&r<=y)return sum[rot];int mid=(l+r)>>1,ret=0;if(x<=mid)ret+=query(ls[rot],l,mid,x,y,opt);if(y>mid)ret+=query(rs[rot],mid+1,r,x,y,opt);return ret;}
}Scol[2],Sdir[2];
I void init(){memset(edge,0,sizeof(edge));memset(ans,0,sizeof(ans));memset(vis,0,sizeof(vis));dsu[0].init(),dsu[1].init(),dsu[2].init();Scol[0].init(),Scol[1].init();Sdir[0].init(),Sdir[1].init();
}
I void Merge(int u,int v){Scol[0].rt[v]=Scol[0].merge(Scol[0].rt[u],Scol[0].rt[v],1,q);Scol[1].rt[v]=Scol[1].merge(Scol[1].rt[u],Scol[1].rt[v],1,q);Sdir[0].rt[v]=Sdir[0].merge(Sdir[0].rt[u],Sdir[0].rt[v],1,n*m);Sdir[1].rt[v]=Sdir[1].merge(Sdir[1].rt[u],Sdir[1].rt[v],1,n*m);
}
I bool eat(int x,int y){return ch[x].col!=ch[y].col&&ch[x].lvl>ch[y].lvl;
}
I void solve(){cin>>n>>m>>q;init();for(int i=1;i<=n;i++){cin>>str+1;for(int j=1;j<m;j++){edge[get1(i,j+1)][1]=str[j]-'0';edge[get1(i,j)][3]=str[j]-'0';}}for(int i=1;i<n;i++){cin>>str+1;for(int j=1;j<=m;j++){edge[get1(i,j)][4]=str[j]-'0';edge[get1(i+1,j)][2]=str[j]-'0';}}for(int i=1;i<=q;i++)cin>>ch[i].col>>ch[i].lvl>>ch[i].x>>ch[i].y,ch[i].id=i,ch2[i]=ch[i];sort(ch2+1,ch2+q+1,[](Chess a,Chess b){return a.lvl==b.lvl?a.id<b.id:a.lvl<b.lvl;});for(int i=1;i<=q;i++)ch[ch2[i].id]=ch2[i],ch[ch2[i].id].lvl=i;for(int i=1;i<=q;i++)vis[get1(ch[i].x,ch[i].y)]=i;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int id=get1(i,j),nxt;if(vis[id])continue;for(int k=1;k<=4;k++){int fx=i+dir[k][0],fy=j+dir[k][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);if(vis[nxt])continue;if(edge[id][k]==2)dsu[k%2+1].merge(id,nxt,0);if(edge[id][k]==3)dsu[0].merge(id,nxt,0);}}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int id1=get1(i,j),id2=get2(i,j),id=dsu[0].fnd(id1),nxt,col;Sdir[0].rt[id]=Sdir[0].insert(Sdir[0].rt[id],1,n*m,id1);Sdir[1].rt[id]=Sdir[1].insert(Sdir[1].rt[id],1,n*m,id2);for(int k=1;k<=4;k++){int fx=i+dir[k][0],fy=j+dir[k][1];if(edge[id1][k]!=3||fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);if(!vis[nxt])continue;col=ch[vis[nxt]].col;Scol[col].rt[id]=Scol[col].insert(Scol[col].rt[id],1,q,ch[vis[nxt]].lvl);}}for(int i=q;i;i--){int fx,fy,nxt,id=get1(ch[i].x,ch[i].y),col=ch[i].col,X=ch[i].x,Y=ch[i].y;int fid1,fid2;for(int j=1;j<=4;j++){if(edge[id][j]!=3)continue;fx=X+dir[j][0],fy=Y+dir[j][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=dsu[0].fnd(get1(fx,fy));Scol[col].remove(Scol[col].rt[nxt],1,q,ch[i].lvl);}for(int j=1;j<=4;j++){if(edge[id][j]!=3)continue;fx=X+dir[j][0],fy=Y+dir[j][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);if(vis[nxt]&&vis[nxt]<i)continue;dsu[0].merge(id,nxt,1);}fid1=dsu[0].fnd(id);ans[i]+=dsu[0].sze[fid1]-1;ans[i]+=Scol[!col].query(Scol[!col].rt[fid1],1,q,1,ch[i].lvl,i==4?1:0);for(int j=1;j<=4;j++){if(edge[id][j]!=2)continue;fx=X+dir[j][0],fy=Y+dir[j][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);if(vis[nxt]&&vis[nxt]<i)continue;dsu[j%2+1].merge(id,nxt,0);}fid2=dsu[2].fnd(id);int maxn,minn;maxn=dsu[2].maxn[fid2],minn=dsu[2].minn[fid2];ans[i]+=maxn-minn+1;ans[i]-=Sdir[0].query(Sdir[0].rt[fid1],1,n*m,minn,maxn);if(edge[minn][1]==2&&eat(i,vis[minn-1])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[minn-1]].lvl,ch[vis[minn-1]].lvl)==0)ans[i]++;if(edge[maxn][3]==2&&eat(i,vis[maxn+1])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[maxn+1]].lvl,ch[vis[maxn+1]].lvl)==0)ans[i]++;fid2=dsu[1].fnd(id);maxn=dsu[1].maxn[fid2],minn=dsu[1].minn[fid2];ans[i]+=(maxn-minn)/m+1;if(edge[minn][2]==2&&eat(i,vis[minn-m])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[minn-m]].lvl,ch[vis[minn-m]].lvl)==0)ans[i]++;if(edge[maxn][4]==2&&eat(i,vis[maxn+m])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[maxn+m]].lvl,ch[vis[maxn+m]].lvl)==0)ans[i]++;fx=getx(maxn),fy=gety(maxn),maxn=get2(fx,fy);fx=getx(minn),fy=gety(minn),minn=get2(fx,fy);ans[i]-=Sdir[1].query(Sdir[1].rt[fid1],1,n*m,minn,maxn);for(int j=1;j<=4;j++){if(edge[id][j]!=1)continue;fx=X+dir[j][0],fy=Y+dir[j][1];if(fx<1||fy<1||fx>n||fy>m)continue;nxt=get1(fx,fy);if(vis[nxt]&&vis[nxt]<i){if(eat(i,vis[nxt])&&Scol[!col].query(Scol[!col].rt[fid1],1,q,ch[vis[nxt]].lvl,ch[vis[nxt]].lvl)==0)ans[i]++;}else if(dsu[0].fnd(id)!=dsu[0].fnd(nxt))ans[i]++;}}for(int i=1;i<=q;i++)cout<<ans[i]<<'\n';
}
int main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);int T;cin>>T;while(T--)solve();return 0;
}