首页 > 其他 > 详细

[HAOI2016] 地图 - 仙人掌,莫队,分块

时间:2020-03-03 20:05:34      阅读:59      评论:0      收藏:0      [点我收藏+]

给定一个仙人掌,每个点有一家拉面店,售卖油腻程度为 \(a_i\) 的拉面,油腻程度相同的拉面称作同一种拉面。有 \(q\) 个询问,每次问从点 \(x\) 开始,不经过所有从 \(x\)\(1\) 号点简单路径上的边,能够访问到的所有点中,油腻程度 \(\leq y\) 且品尝次数为奇数或偶数的拉面有多少种。

技术分享图片

Solution

建圆方树,则问题转化为查询某点为根的子树内,出现次数为奇数或偶数,且颜色编号不大于给定值的颜色总数

在圆方树上对所有圆点跑出 DFS 序,则子树统计转化为区间统计

考虑莫队套值域分块,用 \(cnt[i]\) 记录离散化后值为 \(i\) 的颜色的出现次数,同时维护 \(sum[i][0/1]\) 表示编号为 \(i\) 的块中出现次数为偶数或奇数的颜色的种类数,注意把 \(0\) 特判掉

#include <bits/stdc++.h>
using namespace std;

const int N = 500005;
map<int,int> mp;
int n,m,q,lim,val[N],ind,limv;
int getbel(int x) {
    return x/lim + 1;
}
int getbelv(int x) {
    return x/limv + 1;
}

struct query {
    int l,r,ty,y,id;
    bool operator < (const query &b) {
        if(getbel(l)==getbel(b.l)) return r<b.r;
        else return getbel(l)<getbel(b.l);
    }
};

namespace solver {
    int a[N],cnt[N],sum[N][2],ans[N];
    query s[N];
    void add(int x) {
        if(cnt[x]==0) sum[getbelv(x)][1]++;
        else if(cnt[x]&1) sum[getbelv(x)][1]--, sum[getbelv(x)][0]++;
        else sum[getbelv(x)][1]++, sum[getbelv(x)][0]--;
        cnt[x]++;
    }
    void dec(int x) {
        cnt[x]--;
        if(cnt[x]==0) sum[getbelv(x)][1]--;
        else if(cnt[x]&1) sum[getbelv(x)][1]++, sum[getbelv(x)][0]--;
        else sum[getbelv(x)][1]--, sum[getbelv(x)][0]++;
    }
    int getans(int x,int ty) {
        int ans=0;
        for(int i=1;i<getbelv(x);i++) ans+=sum[i][ty];
        for(int i=getbelv(x)*limv-limv;i<=x;i++) if(cnt[i]%2==ty&&cnt[i]) ans++;//!
        return ans;
    }
    void solve() {
        for(int i=1;i<=n;i++) mp[a[i]]++;
        for(auto i=mp.begin();i!=mp.end();i++) i->second=++ind, val[ind]=i->first;
        for(int i=1;i<=n;i++) a[i]=mp[a[i]];
        lim=sqrt(n);
        limv=sqrt(ind);
        sort(s+1,s+q+1);
        int l=1,r=0;
        for(int i=1;i<=q;i++) {
            while(r<s[i].r) add(a[++r]);
            while(l>s[i].l) add(a[--l]);
            while(r>s[i].r) dec(a[r--]);
            while(l<s[i].l) dec(a[l++]);
            auto it=mp.upper_bound(s[i].y);
            --it;
            ans[s[i].id]=getans(it->second,s[i].ty);
        }
        for(int i=1;i<=q;i++) cout<<ans[i]<<endl;
    }
}

namespace cactus {
    int cnt;
    vector<int> G[N], g[N * 2];
    int dfn[N], low[N], dfc, s[N], t[N], ind, stk[N], tp, a[N];

    void Tarjan(int u) {
        low[u] = dfn[u] = ++dfc;
        stk[++tp] = u;
        for (auto v : G[u]) {
            if (!dfn[v]) {
                Tarjan(v);
                low[u] = min(low[u], low[v]);
                if (low[v] == dfn[u]) {
                    ++cnt;
                    for (int x = 0; x != v; --tp) {
                        x = stk[tp];
                        g[cnt].push_back(x);
                        g[x].push_back(cnt);
                    }
                    g[cnt].push_back(u);
                    g[u].push_back(cnt);
                }
            }
            else low[u] = min(low[u], dfn[v]);
        }
    }

    void dfs(int p) {
        if(p<=n) s[p]=++ind; else s[p]=-1;//!
        for(int q:g[p]) if(s[q]==0) dfs(q);
        if(p<=n) t[p]=ind;
    }

    void solve() {
        cin>>n>>m;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=m;i++) {
            int t1,t2;
            cin>>t1>>t2;
            G[t1].push_back(t2);
            G[t2].push_back(t1);
        }
        cnt = n;
        for (int u = 1; u <= n; ++u)
            if (!dfn[u]) Tarjan(u), --tp;
        dfs(1);
        cin>>q;
        for(int i=1;i<=n;i++) solver::a[s[i]]=a[i];
        for(int i=1;i<=q;i++) {
            int ty,x,y;
            cin>>ty>>x>>y;
            solver::s[i]={s[x],t[x],ty,y,i};
        }
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cactus::solve();
    solver::solve();
}

[HAOI2016] 地图 - 仙人掌,莫队,分块

原文:https://www.cnblogs.com/mollnn/p/12404084.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!