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

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

分析

x x x可以走到的节点仙人掌的子树所有节点。
好像仙人掌子树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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值