1. 程式人生 > >[HDU 6430] 2018 Multi-University Training Contest 10 Problem E. TeaTree 暴力 bitset

[HDU 6430] 2018 Multi-University Training Contest 10 Problem E. TeaTree 暴力 bitset

題目大意

一顆樹, 每個節點上有一個小於等於1e5的數字, 然後每個節點的值等於所有以它為LCA 的節點對(i, j)中gcd(v[i], v[j])的最大值, 要求輸出所有節點的值

思路

一開始想到用bitset記錄每個節點的所有因數, 但TLE了, 沒想到G++的bitset有一個函式叫做_Find_first()能玄學地快速找到為1的最低位, 賽後改用這個函式就AC了
具體思路是後序遍歷這棵樹, 這樣可以保證訪問某節點時, 它的所有子節點都已經訪問過了, 然後每個節點用一個bitset記錄它和它的所有子節點所有因子(要預處理[1, 1e5]所有數字的所有因子)
一開始將節點的bitset設定為本結點v[i]的因子情況, 然後以當前節點為LCA的節點對, 要麼是一個為本節點, 一個為其子節點, 要麼兩個來自其不同的兩個子節點, 兩個bitset相與就能得到其所有公因子, 取最大的一個就是答案
因為每次只需要用的當前節點和其直接子節點的bitset, 當我們處理完當前節點後, 將其所有子節點的bitset刪除, 否則會TLE
要求最大的公因子(即bitset最高的1位置), _Find_first()

, 只能找到最低位為1的位置, 所以一開始就將bitset反過來就好了

程式碼

Accepted 6430 1107MS 18604K 1633 B G++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100, mod = 1e9 + 7, sqrn = 320;
int n;
struct edge
{
    int from, to, nxt;
};
edge es[maxn];
int head[maxn];

vector
<int>
divisor[maxn]; inline void addedge(int id, int from, int to) { es[id].from = from; es[id].to = to; es[id].nxt = head[from]; head[from] = id; } bitset<maxn> *bitv[maxn]; int f[maxn], v[maxn]; int ans[maxn]; void dfs(int u) { for(int i=head[u]; i; i=es[i].nxt) { int
v=es[i].to; dfs(v); } bitv[u] = new bitset<maxn>(0); for(auto &ite : divisor[v[u]]) { (*bitv[u])[maxn-1-ite] = 1; } int x; for(int i=head[u]; i; i=es[i].nxt) { int v = es[i].to; x = ((*bitv[u]) & (*bitv[v]))._Find_first(); if(x<maxn) ans[u] = max(maxn-1-x, ans[u]); (*bitv[u]) |= (*bitv[v]); } for(int i=head[u]; i; i=es[i].nxt) { int v = es[i].to; delete bitv[v]; } } int main() { for (int i = 1; i < maxn; ++i) { for (int j = i; j < maxn; j += i) divisor[j].push_back(i); } scanf("%d", &n); for(int i=2; i<=n; ++i) scanf("%d", f+i); for(int i=1; i<=n; ++i) scanf("%d", v+i); for(int i=2; i<=n; ++i) { addedge(i, f[i], i); } dfs(1); for(int i=1; i<=n; ++i) { if(ans[i] == 0) printf("-1\n"); else printf("%d\n", ans[i]); } return 0; }