欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

bzoj4564: [Haoi2016]地图 仙人掌的圆方树 莫队 分块

程序员文章站 2022-05-22 10:16:32
...

bzoj4564: [Haoi2016]地图

Description

一天rin来到了一个遥远的都市。这个都市有n个建筑,编号从1到n,其中市中心编号为1,这个都市有m条双向通
行的街道,每条街道连接着两个建筑,其中某些街道首尾相连连接成了一个环。rin通过长时间的走访,已经清楚
了这个都市的两个特点:1. 从市中心出发可以到达所有的建筑物。2. 任意一条街道最多存在与一个简单环中。令
rin心花怒放的是,每个建筑物都会有拉面售卖。拉面有很多不同的种类,但对于rin而言只有油腻程度的不同,因
此我们把油腻程度相同的拉面看做同一种拉面。由于不同建筑物的拉面的油腻程度可能不同,我们用一个正整数来
表示拉面的油腻程度。要知道,拉面可是rin的最爱,但是现在到了下班高峰期,都市的交通变得非常的堵塞。 ri
n只能通过没有被堵死的街道通行,去品尝所在建筑物的拉面。现在rin想知道,如果她正在编号为x的建筑物,那
么在从市中心到x的所有简单路径经过的街道都被堵死的情况下,rin可以品尝到的拉面中(注意没有出现的拉面是
不能算在里面的):

  1. 油腻程度≤ y且品尝次数为奇数次的拉面有多少种?
  2. 油腻程度≤ y且品尝次数为偶数次的拉面有多少种?

Input

第一行两个正整数n,m,含义如题所示第二行一共N个正整数,第i个数Ai表示第i个建筑物出售的拉面的油腻程度。
接下来M行,每行两个正整数x,y,表示在建筑物x,y之间有一条双向通行的街道。数据保证1<=x<y<=N接下来一行一
个正整数Q,表示询问个数。接下来Q行每行三个非负整数ty,x,y,x表示询问的建筑物编号,y表示油腻程度的限制
,ty=0时表示询问偶数,ty=1表示询问奇数。N<=100000,M<=150000,Q<=100000,Ai<=10^6提示:请注意数据范围中
的<=,特殊条件中提到的??均为询问中的y,对于所有的数据,有y<=10^6

Output

一共Q行,对于每个询问输出一个答案。

Sample Input

10 12
1 10 4 5 2 10 1 8 4 8
1 2
1 3
1 4
2 5
4 6
4 7
7 8
2 9
8 10
1 6
8 10
4 7
10
0 3 9
1 7 6
0 5 2
1 10 9
0 5 7
1 7 4
0 7 3
1 2 7
0 3 4
0 3 8

Sample Output

0
1
0
1
0
1
0
2
0
0

分析

xx可以走到的节点仙人掌的子树所有节点。
好像仙人掌子树dfs序有一些奇技淫巧来着?
不过我是直接上了圆方树。
剩下的转化为某个区间内小于某个权值的数中出现奇数/偶数次的数的个数。
可以用莫队+分块解决。
做法参见gty的二逼妹子序列

代码

码农题

#include<bits/stdc++.h>
const int N = 2e5 + 10, Bs = 320;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int a[N], in[N], id[N], b[N], out[N], ps[N], l[N], r[N], A[N], tot;
struct Edge {
    int pr[N], to[N << 1], nx[N << 1], tp;
    void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
    void adds(int u, int v) {add(u, v); add(v, u);}
};
struct Round_Square_Tree {
    Edge T; int tot;
    void dfs(int u, int ff) {
        in[u] = ++tot; ps[tot] = u;
        for(int i = T.pr[u]; i; i = T.nx[i])
            if(T.to[i] != ff) dfs(T.to[i], u);
        out[u] = tot;
    }
}rst;
struct Tarjan {
    Edge G; int fa[N], dfn[N], low[N], st[N], tp, tm;
    void dfs(int u, int ff) {
        fa[u] = ff; dfn[u] = low[u] = ++tm; st[++tp] = u;
        for(int i = G.pr[u], v; i; i = G.nx[i]) 
        if((v = G.to[i]) != ff) {
            if(!dfn[v]) {
                dfs(v, u), low[u] = std::min(low[u], low[v]);
                if(low[v] >= dfn[u]) 
                    for(rst.T.adds(u, ++tot); st[tp + 1] != v;)
                        rst.T.adds(st[tp--], tot);
            }
            else low[u] = std::min(low[u], dfn[v]); 
        }
    }
}tar;
struct Block {
    int cnt[Bs]; int odd, sum;
    void Ins(int x, int p) {
        sum -= cnt[x] ? 1 : 0; odd -= cnt[x] & 1; 
        sum += (cnt[x] += p) ? 1 : 0; odd += cnt[x] & 1;
    }
};
struct Ask {bool t; int l, r, y, id;};
bool cmp(Ask a, Ask c) {
    return b[a.l] == b[c.l] ? ((b[a.l] & 1) ? a.r < c.r : a.r > c.r) : b[a.l] < b[c.l];
}
struct MT {
    Block b[Bs]; Ask q[N]; int n;
    void Add(int x, int p) {
        int v; if(!(v = a[ps[x]])) return ;
        b[id[v]].Ins(v - l[id[v]], p);
    }
    int Query(int v, int t) {
        int odd = 0, x = 1, sum = 0; 
        for(;r[x] <= v; ++x) odd += b[x].odd, !t ? sum += b[x].sum : 0;
        for(int i = l[x];i <= v; ++i) 
            odd += b[x].cnt[i - l[x]] & 1, !t ? (sum += b[x].cnt[i - l[x]] ? 1 : 0) : 0;
        return t ? odd : sum - odd;
    }
    void Work() {
        std::sort(q + 1, q + n + 1, cmp);
        int L = 1, R = 0;
        for(int i = 1;i <= n; ++i) {
            for(;R < q[i].r;) Add(++R, 1);
            for(;L > q[i].l;) Add(--L, 1);
            for(;L < q[i].l;) Add(L++, -1);
            for(;R > q[i].r;) Add(R--, -1);
            A[q[i].id] = Query(q[i].y, q[i].t);
        }
    }
}mt;
struct Init {
    int c[N], nn, n, m, B, Q;
    int F(int x) {
        if(x < c[1]) return 0;
        int L = 1, R = nn, m;
        for(;L != R; c[m = L + R + 1 >> 1] <= x ? L = m : R = m - 1) ;
        return L;
    }
    void Value_Init() {
        tot = n = ri(); m = ri();
        for(int i = 1;i <= n; ++i) c[i] = a[i] = ri();
        std::sort(c + 1, c + n + 1);
        nn = 1; for(int i = 2;i <= n; ++i) if(c[i] != c[i - 1]) c[++nn] = c[i];
        for(int i = 1;i <= n; ++i) a[i] = F(a[i]);
        B = sqrt(nn); 
        for(int i = 1;i <= nn; ++i) {
            id[i] = (i - 1) / B + 1;
            if(!l[id[i]]) l[id[i]] = i; r[id[i]] = i;
        }
        r[id[nn] + 1] = l[id[nn] + 1] = nn + 1;
    }
    void Tree_Init() {
        for(;m--;) tar.G.adds(ri(), ri());
        tar.dfs(1, 0); rst.dfs(1, 0);
        B = sqrt(tot) + 1;
        for(int i = 1;i <= tot; ++i) b[i] = (i - 1) / B + 1;
        Q = ri();
        for(int i = 1;i <= Q; ++i) {
            int t = ri(), u = ri(), y = ri();
            y = F(y); if(!y) continue;
            mt.q[++mt.n] = (Ask) {t, in[u], out[u], y, i};
        }
    }
}pre;
int main() {
    pre.Value_Init(); 
    pre.Tree_Init();
    mt.Work();
    for(int i = 1;i <= pre.Q; ++i) printf("%d\n", A[i]);
    return 0;
}