专栏文章

题解:P10834 [COTS 2023] 题 Zadatak

P10834题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min2dluf
此快照首次捕获于
2025/12/01 19:26
3 个月前
此快照最后确认于
2025/12/01 19:26
3 个月前
查看原文
线段树合并忘记怎么写了,所以场上写的是启发式合并。
首先我们要手玩小数据,找到图形拼一起有什么性质。容易发现:
  • 黑色重叠部分会被变成白色,所以两个图形拼一起的代价就是两倍黑色重叠部分的面积。
  • 同理,两个图形拼一起之后,用两个图形的黑色部分面积之和,减去拼一起产生的代价,就是新图形黑色部分的面积。
  • 如果一个图形包含的正方形边长集合为 SS,假设 SS 从大到小排序后形成的序列为 AA,则该图形黑色部分面积为 A12A22+A32A42=i=1A(1)i1Ai2A_1^2-A_2^2+A_3^2-A_4^2\dots=\sum\limits_{i=1}^{|A|}(-1)^{i-1}A_i^2,加入顺序无影响。
那我们需要在启发式合并的同时,维护一个数据结构,可以快速维护一个集合 SS 的如下操作:
  • SS 中加入一个元素。
  • SS 从大到小排序后,查询 i=1S(1)i1Si2\sum\limits_{i=1}^{|S|}(-1)^{i-1}S_i^2 的值。
可以用动态开点权值线段树维护,每个节点维护三个关键量:
  • cntcnt,当前值域区间内有多少个元素。
  • c0,c1c0,c1,对当前值域区间内的元素从小到大,从 11 开始标号,偶(奇)数位置上元素的平方和。
时间复杂度 Θ(nlog2n)\Theta(n\log^2n)
注意这个做法有点卡空间,如果直接写只能拿 6060 分。在将 yy 合并到 xx 的时候,要释放 yy 的内存。以下给出对 unordered_mapvector 释放内存的代码:
CPP
mp.clear();
mp.rehash(0);
vec.clear();
vec.shrink_to_fit();
完整代码如下:
CPP
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define fr first
#define sc second
#define pii pair<int,int>
#define yes cout<<"Yes\n"
#define no cout<<"No\n"
#define fo(i,l,r) for(int i=l;i<=r;i++)
#define ro(i,r,l) for(int i=r;i>=l;i--)
const int N=1e5+5,V=1e6;
int n,q,id[N<<1];
ll a[N];
namespace dsu{
    #define mid ((l+r)>>1)
    struct sgm{
        struct node{
            int lc,rc,s;
            ll s0,s1;
        };
        int c=1;
        vector<node>d;
        sgm(){
            d.push_back({0,0,0,0,0});
            d.push_back({0,0,0,0,0});
        }
        void modify(int x,int l,int r,int t,int k){
            if (l==r){
                d[x].s1+=k*1ll*l*l;
                d[x].s+=k;
                return;
            }
            if (t<=mid){
                if (!d[x].lc){
                    d[x].lc=++c;
                    d.push_back({0,0,0,0,0});
                }
                modify(d[x].lc,l,mid,t,k);
            }
            else{
                if (!d[x].rc){
                    d[x].rc=++c;
                    d.push_back({0,0,0,0,0});
                }
                modify(d[x].rc,mid+1,r,t,k);
            }
            d[x].s=d[d[x].lc].s+d[d[x].rc].s;
            d[x].s0=d[d[x].lc].s0+((d[d[x].lc].s&1)?d[d[x].rc].s1:d[d[x].rc].s0);
            d[x].s1=d[d[x].lc].s1+((d[d[x].lc].s&1)?d[d[x].rc].s0:d[d[x].rc].s1);
        }
    }sg[N];
    unordered_map<int,int>val[N];
    void insert(int x,int k){
        if (!val[x].count(k)){
            sg[x].modify(1,1,V,k,1);
            val[x][k]=1;
        }
        else{
            sg[x].modify(1,1,V,k,-1);
            val[x].erase(k);
        }
    }
    ll merge(int x,int y){
        if (val[x].size()<val[y].size())
            swap(x,y);
        id[++n]=x;
        for (auto i:val[y])
            insert(x,i.fr);
        val[y].clear();
        val[y].rehash(0);
        sg[y].d.clear();
        sg[y].d.shrink_to_fit();
        return abs(sg[x].d[1].s0-sg[x].d[1].s1);
    }
}
using namespace dsu;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n,q=n-1;
    fo(i,1,n){
        cin>>a[i];
        id[i]=i;
        insert(i,a[i]);
    }
    while (q--){
        int x,y;
        cin>>x>>y;
        x=id[x],y=id[y];
        ll rs=0;
        rs+=abs(sg[x].d[1].s0-sg[x].d[1].s1);
        rs+=abs(sg[y].d[1].s0-sg[y].d[1].s1);
        ll rt=merge(x,y);
        rs-=rt,rs/=2ll;
        cout<<rs<<'\n';
    }
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...