P10573 [JRKSJ R8] C0mp0nents 题解

P10573 [JRKSJ R8] C0mp0nents

思路

个人感觉直接照着题解看有点难以理解。

首先我们将点按 \(\mod k\) 的余数划分出来,显然只有这些点相互之间才可能会有贡献。
从小到大列出所有点后,我们考虑如何快速统计当每个点作为 \(s\) 的时候的答案。显然可以合并的答案区间在序列上是连续的。
我们假设这个区间是 \([l,r]\)

我们还是将其分成经典的三条限制:

  1. \([l,r]\) 联通
  2. 每个 \(x\in (l,s)\),都至少与 \([l,x)\) 有一条连边。
  3. 每个 \(x\in (s,r)\),都至少与 \((x,r]\) 有一条连边。

我们先处理后两个限制。我么设 \(lp_i,rp_i\) 分别表示 \(i-1\)\(i+1\) 向左与向右最远可以分别满足后两条限制的位置。发现这两个东西分别在从小到大、从大到小的意义下单调不升。
于是我们可以通过预处理出每个点向左与向右最近的连边是什么,然后根据定义来维护 \(lp,rp\)

然后考虑第一条限制。我们考虑对于每一条边可能将那些区间连接起来。我们预处理出每个点向后与向前最远的满足 \(lp_j\le i\)\(rp_j\ge i\) 的位置,我们分别记录为 \(suf_i,pre_i\)
假设对于一条边 \((u,v)\),其在序列上的对应的位置为 \(i,j\),其中 \(i<j\)。那么显然这条边可以连接某些点左右区间的地方就是 \([\max(u+1,pre_v),\min(v-1,suf(u))]\)

然后统计答案就是判断连通性。分类讨论即可。

code

感觉说的太抽象了,但是好像也没有很好的形象的描述方法。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
int n,m,k,lp[N],rp[N],pre[N],suf[N],l[N],r[N],stk[N],id[N],ans[N],s[N],cnt;
vector <int> q[N];
void solve(int x){
    cnt=n/k+(n%k>=x);lp[1]=1,rp[cnt]=cnt;suf[0]=0,pre[cnt+1]=cnt+1;    //记得补全预处理和清空
    for(int i=1;i<=cnt;i++)stk[i]=(i-1)*k+x,id[stk[i]]=i;
    for(int i=1;i<=cnt;i++){
        int u=stk[i];l[i]=0,r[i]=cnt+1;
        for(int v:q[u]){int j=id[v];j<i?l[i]=max(l[i],j):r[i]=min(r[i],j);}
    }
    for(int i=2;i<=cnt;i++){lp[i]=lp[i-1];if(l[i-1]<lp[i])lp[i]=i-1;}
    for(int i=cnt-1;i>=1;i--){rp[i]=rp[i+1];if(r[i+1]>rp[i])rp[i]=i+1;}                  //注意这里的 lp 和 rp 分别是 i 的前面一个点和后面一个点的
    for(int i=1;i<=cnt;i++){suf[i]=suf[i-1];while(lp[suf[i]+1]<=i&&suf[i]<cnt)suf[i]++;} //注意这个地方是 suf[i]+1!!!这个就保证在后面差分的时候的正确性
    for(int i=cnt;i>=1;i--){pre[i]=pre[i+1];while(rp[pre[i]-1]>=i&&pre[i]>1)pre[i]--;}
    for(int i=1;i<=cnt;i++){
        int u=stk[i];
        for(int v:q[u]){
            int j=id[v],ql=max(i+1,pre[j]),qr=min(j-1,suf[i]);if(j<i||qr<ql) continue;
            s[ql]++,s[qr+1]--;
        }
    }
    for(int i=1;i<=cnt;i++){
        s[i]+=s[i-1];
        if(l[i]<lp[i]&&r[i]>rp[i])lp[i]=rp[i]=i;//向两边都不联通
        else {if(!s[i]){if(l[i]<lp[i])lp[i]=i;else {if(r[i]>rp[i])rp[i]=i;}}}
        ans[stk[i]]=rp[i]-lp[i]+1;
    }
}
void init(){
    for(int i=0;i<=cnt+2;i++) s[i]=lp[i]=rp[i]=l[i]=r[i]=suf[i]=pre[i]=0;
    cnt=0;
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>n>>m>>k;
    for(int i=1,u,v;i<=m;i++){
        cin>>u>>v;if(u%k!=v%k)continue;
        q[u].push_back(v),q[v].push_back(u);
    }
    for(int i=1;i<=k;i++) solve(i),init();
    for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
    return 0;
}
posted @ 2025-05-29 12:56  all_for_god  阅读(14)  评论(0)    收藏  举报