[P9261] Płótno

模拟赛遇到的,好题。

给你一张 \(2\times n\) 的网格图,首尾相连,定义 \(val(l,r)(l\le r)\) 表示颜色编号在 \([l,r]\) 内的格子的四连通块数量,求 \(i\in [1,k],val(l,r)=i\)\(l,r\) 的对数。

参考森林的联通块数量等于点数减边数,从小到大扫描 \(r\),用权值线段树维护 \(val(i,r)\),把 \([1,r]\) 区间加 \(1\) 表示加入一个点,把和 \(r\) 相邻的点 \(x\)\([1,x]\) 区间减 \(1\) 表示增加一条边。

但这么胡乱加边肯定会出问题,有两种连出环的情况。

  1. 出现了一个 \(2\times 2\) 的连通块,会多出一条额外的边,把贡献加回来即可。
  2. 出现了一个首尾相连的大环,这时实际连通块数一定是 \(1\),但我们算出来会是 \(0\),在最后把 \(val=0\) 的答案加到 \(1\) 上去即可。

现在求区间 \(val=x(x\in [0,k])\) 的数量,说这个不能 \(\text{polylog}\) 纯尬黑啊,因为 \(k\) 只有 \(10\),维护一下区间 \(\min\)\(\min\)\(\min+k\) 的数量即可。

最后推荐一道很像的题 CF193D。

code

const int N = 5e5+55;
struct Node {
    int mn,cnt[12];
    Node() : mn(0) { memset(cnt,0,sizeof cnt); }
    friend Node operator+(const Node &p,const Node &q) {
        Node t{}; t.mn=min(p.mn,q.mn);
        for(int i=p.mn;i<t.mn+12;i++) t.cnt[i-t.mn]+=p.cnt[i-p.mn];
        for(int i=q.mn;i<t.mn+12;i++) t.cnt[i-t.mn]+=q.cnt[i-q.mn];
        return t;
    }
};
struct segment_tree {
    #define mid ((L+R)>>1)
    #define ls (u<<1)
    #define rs (ls|1)
    Node tr[N<<2]; int tag[N<<2];
    void clac(int u,int k) { tr[u].mn+=k,tag[u]+=k; }
    void pushdown(int u) {
        if(tag[u]) clac(ls,tag[u]),clac(rs,tag[u]),tag[u]=0;
    }
    void build(int u,int L,int R) {
        tag[u]=0,tr[u].mn=0,tr[u].cnt[0]=R-L;
        if(R-L==1) return ;
        build(ls,L,mid),build(rs,mid,R);
    }
    void update(int u,int L,int R,int l,int r,int k) {
        if(l>=r) return ;
        if(l<=L&&R<=r) return clac(u,k);
        pushdown(u);
        if(l<mid) update(ls,L,mid,l,r,k);
        if(r>mid) update(rs,mid,R,l,r,k);
        tr[u]=tr[ls]+tr[rs];
    }
    Node query(int u,int L,int R,int l,int r) {
        if(l<=L&&R<=r) return tr[u];
        pushdown(u);
        if(r<=mid) return query(ls,L,mid,l,r);
        if(l>=mid) return query(rs,mid,R,l,r);
        return query(ls,L,mid,l,r)+query(rs,mid,R,l,r);
    }
    #undef mid
    #undef ls
    #undef rs
} T;
void Solve() {
    int n,K; cin>>n>>K;
    vv<int> a(2,vec<int>(n));
    vec<pair<int,int>> pos(n<<1);
    for(int i:{0,1}) for(int j=0;j<n;j++)
        cin>>a[i][j],pos[--a[i][j]]={i,j};
    vv<int> e(n<<1),q(n<<1);
    for(int i=0;i<n;i++)
        e[max(a[0][i],a[1][i])].push_back(min(a[0][i],a[1][i]));
    for(int i=0;i<n-(n==2);i++) { int p=(i+1)%n;
        e[max(a[0][i],a[0][p])].push_back(min(a[0][i],a[0][p]));
        e[max(a[1][i],a[1][p])].push_back(min(a[1][i],a[1][p]));
        q[max({a[0][i],a[1][i],a[0][p],a[1][p]})].
         push_back(min({a[0][i],a[1][i],a[0][p],a[1][p]}));
    }
    T.build(1,0,n<<1);
    vec<ll> ans(K);
    auto upd = [&](int x,int k) { T.update(1,0,n<<1,0,x+1,k); };
    for(int r=0;r<n<<1;r++) {
        auto [x,y]=pos[r];
        int lc=(y-1+n)%n,rc=(y+1)%n;
        upd(r,1);
        for(auto l:e[r]) upd(l,-1);
        for(auto l:q[r]) upd(l,1);
        auto now=T.query(1,0,n<<1,0,r+1);
        for(int i=0;i<12&&now.mn+i<=K;i++) ans[max(1,now.mn+i)-1]+=now.cnt[i];
    }
    for(int i=0;i<K;i++) cout<<ans[i]<<' '; cout<<'\n';
}
posted @ 2026-09-02 20:10  ASnown  阅读(3)  评论(0)    收藏  举报