1. 程式人生 > >bzoj4564: [Haoi2016]地圖 仙人掌的圓方樹 莫隊 分塊

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

可以走到的節點仙人掌的子樹所有節點。
好像仙人掌子樹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;
}