[HDU5799] This world need more Zhu [2016 Multi-University Training Contest 6(2016多校聯合訓練2) 1007]


題意

給定一棵樹,n個節點,每個節點有個權值。
詢問:

  • 子樹 u 中出現 a 次的權值的和與出現 b 次的權值的和的 gcd
  • (u,v) 中出現 a 次的權值的和與出現 b 次的權值的和的 gcd

有些繞,總之是每次詢問的兩個數字的 gcd

題解

首先對權值進行離散化。
考慮子樹詢問 經典的莫隊分塊即可。具體實現為求 dfs 序,對詢問排序,該子樹區間左端點所在塊為第一關鍵字,該子樹區間右端點為第二關鍵字,然后查詢。
考慮樹鏈詢問經典的樹上莫隊即可。具體實現為 dfs 分塊,對詢問排序,該樹鏈左端點所在塊為第一關鍵字,該樹鏈右端點dfs序為第二關鍵字,然后查詢。

才知道樹上莫隊有兩種姿勢,一種是我很早學的 vfk 的求 LCA 姿勢,這個題也是這種姿勢。另一種是 std 的姿勢,不用求 LCA ,在 move 同時找到需要翻轉的點。這樣就除去了倍增 LCA 的復雜度,這個姿勢有待學習研究。

代碼

LCA版4336MS

/****************************************\
* Author : ztx
* Title : This world need more Zhu 這個世界需要更多的^(* ̄(oo) ̄)^
* ALG : 樹上莫隊+dfs序莫隊分塊
* CMT : 似乎樹上莫隊有兩種姿勢,今天寫的vfk的LCA版,然而還有一個std的更優的姿勢待研究
* Time :
\****************************************/


#include <cstdio>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
if (CH == '-') NEG = true , CH = getchar() ;
while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
if (NEG) ret = -ret ;
}
template <typename TP>inline void readc(TP& ret) {
while (ret=getchar() , ret<'!') ;
while (CH=getchar() , CH>'!') ;
}
template <typename TP>inline void reads(TP *ret) {
ret[0]=0;while (CH=getchar() , CH<'!') ;
while (ret[++ret[0]]=CH,CH=getchar(),CH>'!') ;
ret[ret[0]+1]=0;
}

#include <algorithm>
#include <cmath>
#include <cstring>

#define maxn 100010LL
#define maxm 100010LL

ll gcd(ll a,ll b) { return b ? gcd(b,a%b) : a ; }

int to[maxn<<1], nxt[maxn<<1], star[maxn], tote;
inline void AddEdge(int u,int v) {
tote ++ ; to[tote] = v; nxt[tote] = star[u]; star[u] = tote;
}

int n, m;
int col[maxn], dis[maxn], dis_cnt, newcol[maxn];

int sta[maxn], top = 0, idx, in[maxn], out[maxn], point[maxn], sz[maxn];
int szblc, blc_cnt, dep[maxn], fa[18][maxn], belong[maxn], lca[maxm];
ll sum[maxn], ans[maxn];
int cnt[maxn];
bool vis[maxn];

void dfs(int u,int fa) {
in[u] = ++ idx, point[idx] = u, dep[u] = dep[fa] + 1, ::fa[0][u] = fa, sz[u] = 0;
for (int p = star[u], v; v = to[p], p; p = nxt[p])
if (v != fa && (dfs(v,u), sz[u] += sz[v], sz[u] > szblc)) {
++ blc_cnt;
while (sz[u] -- ) belong[sta[top--]] = blc_cnt;
}
out[u] = idx, sta[++top] = u, sz[u] ++ ;
}

inline void ST() {
int i, k;
Rep (k,1,17) Rep (i,0,n)
fa[k][i] = fa[k-1][fa[k-1][i]] ;
}

inline int LCA(int u,int v) {
int k;
if (dep[u] < dep[v]) u ^= v, v ^= u, u ^= v;
Rev (k,17,0)
if (dep[fa[k][u]] >= dep[v]) u = fa[k][u];
if (u == v) return u;
Rev (k,17,0)
if (fa[k][u] != fa[k][v]) u = fa[k][u], v = fa[k][v];
return fa[0][u];
}

struct QUERY {
int l, r, belong, cmp, a, b, id, lca;
inline void set(int mode,int u,int v,int a,int b,int id) {
this->a = a, this->b = b, this->id = id;
if (mode&1) {
l = in[u], r = out[u], belong = in[u] / szblc, cmp = r;
} else {
if (u > v) u ^= v, v ^= u, u ^= v;
l = u, r = v, belong = ::belong[u], cmp = in[r];
lca = LCA(u,v);
}
}
bool operator < (const QUERY&B) const {
if (belong == B.belong) return cmp < B.cmp; return belong < B.belong;
}
}qs[maxm], qc[maxm];
int cnt_s, cnt_c;

inline void Insert(int u) {
sum[cnt[newcol[u]]] -= col[u], sum[++cnt[newcol[u]]] += col[u];
}

inline void Erase(int u) {
sum[cnt[newcol[u]]] -= col[u], sum[--cnt[newcol[u]]] += col[u];
}

inline void Reverse(int u) {
if (!vis[u]) { vis[u] = true; Insert(u); }
else { vis[u] = false; Erase(u); }
}

inline void MoveTo(int u,int v) {
if (dep[u] < dep[v]) u ^= v, v ^= u, u ^= v;
while (dep[u] > dep[v]) Reverse(u), u = fa[0][u];
while (u != v)
Reverse(u), Reverse(v), u = fa[0][u], v = fa[0][v];
}

inline void GetAns(int i) {
Reverse(qc[i].lca) ;
ans[qc[i].id] = gcd(sum[qc[i].a],sum[qc[i].b]);
Reverse(qc[i].lca) ;
}


int main() {
#define READ
#ifdef READ
freopen("data.in" ,"r",stdin ) ;
freopen("me.out","w",stdout) ;
#endif
int T, kiss, i, mode, u, v, a, b, l, r;
for (read(T), kiss = 1;kiss <= T;kiss ++ ) {
printf("Case #%d:\n", kiss);
read(n), read(m);
szblc = 333LL;
/// read color
Rep (i,1,n)
read(col[i]), dis[i] = col[i];
/// discrete
std::sort(dis+1, dis+n+1);
dis_cnt = std::unique(dis+1,dis+n+1)-dis;
Rep (i,1,n)
newcol[i] = std::lower_bound(dis+1,dis+dis_cnt+1,col[i])-dis;
/// read edge
tote = 0;
memset(star, 0, sizeof star);
rep (i,1,n)
read(u), read(v), AddEdge(u,v), AddEdge(v,u);
/// dfs get tree's Modui Blocks
idx = blc_cnt = top = 0;
dep[0] = 0;
dfs(1,0);
blc_cnt ++ ;
while (top) belong[sta[top--]] = blc_cnt;
ST();
/// read query
cnt_s = cnt_c = 0;
Rep (i,1,m) {
read(mode), read(u), read(v), read(a), read(b);
if (mode&1) qs[++cnt_s].set(mode,u,v,a,b,i);
else qc[++cnt_c].set(mode,u,v,a,b,i);
}
std::sort(qs+1,qs+cnt_s+1);
std::sort(qc+1,qc+cnt_c+1);
/// query subtree
memset(cnt,0,sizeof cnt);
memset(sum,0,sizeof sum);
l = 1, r = 0;
Rep (i,1,cnt_s) {
while (l > qs[i].l) Insert(point[--l]);
while (r < qs[i].r) Insert(point[++r]);
while (l < qs[i].l) Erase(point[l++]);
while (r > qs[i].r) Erase(point[r--]);
ans[qs[i].id] = gcd(sum[qs[i].a],sum[qs[i].b]);
}
/// query chain
memset(cnt,0,sizeof cnt);
memset(sum,0,sizeof sum);
memset(vis,0,sizeof vis);
if (cnt_c)
MoveTo(l = qc[1].l,r = qc[1].r), GetAns(1) ;
Rep (i,2,cnt_c) {
MoveTo(l,qc[i].l), MoveTo(r,qc[i].r), GetAns(i);
l = qc[i].l, r = qc[i].r;
}
/// output
Rep (i,1,m) printf("%lld\n", ans[i]);
}
#ifdef READ
fclose(stdin) ; fclose(stdout) ;
#else
getchar() ; getchar() ;
#endif
return 0 ;
}

std版2870MS

/****************************************\
* Author : ztx
* Title : This world need more Zhu 這個世界需要更多的^(* ̄(oo) ̄)^
* ALG : 樹上莫隊+dfs序莫隊分塊
* CMT : 似乎樹上莫隊有兩種姿勢,今天寫的vfk的LCA版,然而還有一個std的更優的姿勢待研究
這是std姿勢的
* Time :
\****************************************/


#include <cstdio>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
if (CH == '-') NEG = true , CH = getchar() ;
while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
if (NEG) ret = -ret ;
}
template <typename TP>inline void readc(TP& ret) {
while (ret=getchar() , ret<'!') ;
while (CH=getchar() , CH>'!') ;
}
template <typename TP>inline void reads(TP *ret) {
ret[0]=0;while (CH=getchar() , CH<'!') ;
while (ret[++ret[0]]=CH,CH=getchar(),CH>'!') ;
ret[ret[0]+1]=0;
}

#include <algorithm>
#include <cmath>
#include <cstring>

#define maxn 100010LL
#define maxm 100010LL

ll gcd(ll a,ll b) { return b ? gcd(b,a%b) : a ; }

int to[maxn<<1], nxt[maxn<<1], star[maxn], tote;
inline void AddEdge(int u,int v) {
tote ++ ; to[tote] = v; nxt[tote] = star[u]; star[u] = tote;
}

int n, m;
int col[maxn], dis[maxn], dis_cnt, newcol[maxn];

int sta[maxn], top = 0, idx, in[maxn], out[maxn], point[maxn], sz[maxn];
int szblc, blc_cnt, dep[maxn], fa[maxn], belong[maxn];
ll sum[maxn], ans[maxn];
int cnt[maxn];
bool vis[maxn];

void dfs(int u,int fa) {
in[u] = ++ idx, point[idx] = u, dep[u] = dep[fa] + 1, ::fa[u] = fa, sz[u] = 0;
for (int p = star[u], v; v = to[p], p; p = nxt[p])
if (v != fa && (dfs(v,u), sz[u] += sz[v], sz[u] > szblc)) {
++ blc_cnt;
while (sz[u] -- ) belong[sta[top--]] = blc_cnt;
}
out[u] = idx, sta[++top] = u, sz[u] ++ ;
}

struct QUERY {
int l, r, belong, cmp, a, b, id;
inline void set(int mode,int u,int v,int a,int b,int id) {
this->a = a, this->b = b, this->id = id;
if (mode&1) {
l = in[u], r = out[u], belong = in[u] / szblc, cmp = r;
} else {
if (u > v) u ^= v, v ^= u, u ^= v;
l = u, r = v, belong = ::belong[u], cmp = in[r];
}
}
bool operator < (const QUERY&B) const {
if (belong == B.belong) return cmp < B.cmp; return belong < B.belong;
}
}qs[maxm], qc[maxm];
int cnt_s, cnt_c;

inline void Insert(int u) {
sum[cnt[newcol[u]]] -= col[u], sum[++cnt[newcol[u]]] += col[u];
}

inline void Erase(int u) {
sum[cnt[newcol[u]]] -= col[u], sum[--cnt[newcol[u]]] += col[u];
}

inline void Reverse(int u) {
if (!vis[u]) { vis[u] = true; Insert(u); }
else { vis[u] = false; Erase(u); }
}

int cross;

inline void MoveUp(int&u) {
if (!cross) {
if (vis[u] && !vis[fa[u]]) cross = u;
else if (!vis[u] && vis[fa[u]]) cross = fa[u];
}
Reverse(u), u = fa[u];
}

inline void MoveTo(int u,int newu) {
if (u == newu) return ;
cross = 0;
if (vis[newu]) cross = newu;
while (dep[u] > dep[newu]) MoveUp(u);
while (dep[newu] > dep[u]) MoveUp(newu);
while (u != newu) MoveUp(u), MoveUp(newu);
Reverse(u), Reverse(cross);
}

int main() {
#define READ
#ifdef READ
freopen("data.in" ,"r",stdin ) ;
freopen("me.out","w",stdout) ;
#endif
int T, kiss, i, mode, u, v, a, b, l, r;
for (read(T), kiss = 1;kiss <= T;kiss ++ ) {
printf("Case #%d:\n", kiss);
read(n), read(m);
szblc = 333LL;
/// read color
Rep (i,1,n)
read(col[i]), dis[i] = col[i];
/// discrete
std::sort(dis+1, dis+n+1);
dis_cnt = std::unique(dis+1,dis+n+1)-dis;
Rep (i,1,n)
newcol[i] = std::lower_bound(dis+1,dis+dis_cnt+1,col[i])-dis;
/// read edge
tote = 0;
memset(star, 0, sizeof star);
rep (i,1,n)
read(u), read(v), AddEdge(u,v), AddEdge(v,u);
/// dfs get tree's Modui Blocks
idx = blc_cnt = top = 0;
dep[0] = 0;
dfs(1,0);
blc_cnt ++ ;
while (top) belong[sta[top--]] = blc_cnt;
/// read query
cnt_s = cnt_c = 0;
Rep (i,1,m) {
read(mode), read(u), read(v), read(a), read(b);
if (mode&1) qs[++cnt_s].set(mode,u,v,a,b,i);
else qc[++cnt_c].set(mode,u,v,a,b,i);
}
std::sort(qs+1,qs+cnt_s+1);
std::sort(qc+1,qc+cnt_c+1);
/// query subtree
memset(cnt,0,sizeof cnt);
memset(sum,0,sizeof sum);
l = 1, r = 0;
Rep (i,1,cnt_s) {
while (l > qs[i].l) Insert(point[--l]);
while (r < qs[i].r) Insert(point[++r]);
while (l < qs[i].l) Erase(point[l++]);
while (r > qs[i].r) Erase(point[r--]);
ans[qs[i].id] = gcd(sum[qs[i].a],sum[qs[i].b]);
}
/// query chain
memset(cnt,0,sizeof cnt);
memset(sum,0,sizeof sum);
memset(vis,0,sizeof vis);
l = r = 1;
Reverse(1);
Rep (i,1,cnt_c) {
MoveTo(l,qc[i].l), MoveTo(r,qc[i].r);
l = qc[i].l, r = qc[i].r;
ans[qc[i].id]=gcd(sum[qc[i].a],sum[qc[i].b]);
}
/// output
Rep (i,1,m) printf("%lld\n", ans[i]);
}
#ifdef READ
fclose(stdin) ; fclose(stdout) ;
#else
getchar() ; getchar() ;
#endif
return 0 ;
}

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com