1. 程式人生 > >【洛谷】題解 P3180 【[HAOI2016]地圖】

【洛谷】題解 P3180 【[HAOI2016]地圖】

首先詢問是在一棵仙人掌上進行

對原圖進行一次dfs,得到一棵dfs樹

對於每個環,稱環上的點中在dfs樹裡深度最小的那個點為該環的環根

考慮原問題的詢問,有一個約束,是從1號點到x的所有簡單路徑都不能通過

那麼,對於所有經過點x的環來說,除非x是該點環根,否則該環其它點對答案都沒有貢獻

因為可以從環根走到該點下面再走上來,或者環根直接走到這個點,這樣環上的點就都作廢了==

針對這個性質可以將原仙人掌重建

對於每個環,將環上除環根每個點的父親指定為環根而刪去原來指向父親的邊

對新樹進行一次dfs,得到dfs序,這樣是一個序列

每個詢問就可以變成區間詢問了。。莫隊解決

維護權值的方案,用bzoj3809的分塊方法即可

當然,構建新樹不需要真的構建出,兩邊dfs配合tarjan就行了

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn =
1E5 + 10; const int N = 1E6 + 10; int n, m, sq1, sq2, dfs_clock, tot, cur, low[maxn], DFN[maxn], Name[maxn], dfn[maxn], s[maxn], a[maxn], siz[maxn], sum[1010][2], cnt[N], a ns[maxn]; vector <int> v[maxn]; int Getpos (int x, int sq) { return (x % sq == 0) ? x / sq : x / sq + 1; } struct Query {
int l, r, t, y, num; Query(){} Query(int l, int r, int t, int y, int num) : l(l), r(r), t(t), y(y), num(num){} bool operator < (const Query &B) const { if (Getpos(l, sq1) < Getpos(B.l,sq1)) return 1; if (Getpos(l, sq1) > Getpos(B.l,sq1)) return 0; return r < B.r; } } Q[maxn]; void Dfs1(int x, int from) { DFN[x] = low[x] = ++dfs_clock; Name[DFN[x]] = x; for (int i = 0; i < v[x].size(); i++) { int to = v[x][i]; if (to == from) continue; if (!DFN[to]) { Dfs1(to, x); low[x] = min(low[x], low[to]); } else low[x] = min(low[x], DFN[to]); } } void Dfs2(int x, int from) { dfn[x] = ++dfs_clock; siz[x] = 1; for (int i = 0; i < v[x].size(); i++) { int to = v[x][i]; if (to == from) continue; if (!dfn[to] && low[to] >= DFN[x]) { Dfs2(to, x); siz[x] += siz[to]; } } for (int i = 0; i < v[x].size(); i++) { int to = v[x][i]; if (to == from) continue; if (!dfn[to] && low[to] < DFN[x]) { Dfs2(to, x); siz[Name[low[to]]] += siz[to]; } } } void Add(int x) { int pos = Getpos(x, sq2); if (cnt[x] & 1) --sum[pos][1], ++sum[pos][0]; else if (cnt[x]) --sum[pos][0], ++sum[pos][1]; else ++sum[pos][1]; ++cnt[x]; } void Dec(int x) { int pos = Getpos(x, sq2); if (cnt[x] == 1) --sum[pos][1]; else if (cnt[x] & 1) --sum[pos][1], ++sum[pos][0]; else --sum[pos][0], ++sum[pos][1]; --cnt[x]; } int getint() { char ch = getchar(); int ret = 0; while (ch < '0' || '9' < ch) ch = getchar(); while ('0' <= ch && ch <= '9') ret = ret * 10 + ch - '0', ch = getchar(); return ret; } int main() { #ifdef DMC freopen("DMC.txt", "r", stdin); #endif n = getint(); m = getint(); for (int i = 1; i <= n; i++) a[i] = getint(), cur = max(cur, a[i]); sq1 = sqrt(n); sq2 = sqrt(cur); while (m--) { int x = getint(), y = getint(); v[x].push_back(y); v[y].push_back(x); } Dfs1(1, 0); dfs_clock = 0; Dfs2(1, 0); for (int i = 1; i <= n; i++) s[dfn[i]] = a[i]; m = getint(); for (int i = 1; i <= m; i++) { int x, y, t = getint(); x = getint(); y = getint(); Q[i] = Query(dfn[x], dfn[x] + siz[x] - 1, t, y, i); } sort(Q + 1, Q + m + 1); int L = 1, R = 0; for (int i = 1; i <= m; i++) { while (R < Q[i].r) Add(s[++R]); while (L > Q[i].l) Add(s[--L]); while (R > Q[i].r) Dec(s[R--]); while (L < Q[i].l) Dec(s[L++]); int Ans = 0, po = Getpos(Q[i].y, sq2); for (int j = 1; j < po; j++) Ans += sum[j][Q[i].t]; for (int j = po * sq2 - sq2 + 1; j <= Q[i].y; j++) { if (!cnt[j]) continue; Ans += ((cnt[j] & 1) == Q[i].t) ? 1 : 0; } ans[Q[i].num] = Ans; } for (int i = 1; i <= m; i++) printf("%d\n",ans[i]); return 0; }