HDU 4777 思維 樹狀陣列

2021-07-06 04:45:02 字數 1870 閱讀 5689

題意:

一些數,一些詢問。

詢問一段區間內,和其他數都互質的數有幾個。

注意1和所有數包括1本身互質。

思路:

基本參照

首先乙個樸素的想法是暴力。

然後就想到應該離線處理所有的詢問。關鍵是對於乙個詢問,怎麼知道在這個區間內乙個數有沒有對答案做出貢獻。模擬賽的時候想到這裡也就想不下去了,因為感覺可能和質因數分解有關,而質因數分解的時候又想到容斥原理那邊瞬間感覺腦袋**……

其實只要統計乙個數的有效區間就可以,具體就是和他左邊最近最大公約數不為1 — 右邊最大公約數不為1的區間……

統計的話比較巧妙,用乙個訪問陣列統計質因子出現與否與出現位置就可以找到最靠近的左邊在哪,同理逆向的過程可以找到右邊在哪。然後用維護區間的方式更新區間和就行,這裡採用樹狀陣列。

樹狀陣列維護的時候有小技巧,設區間為[l,r],l以後的加1,r以後的減1。 如果乙個數遍歷過不再使用了,它的右區間r以後都加1,這樣可以避免輸出答案時少1(假設詢問的區間[l2,r2],此次去除的區間[l1,r1],l2 > l1 && l1 < r1的話,就會出現這種情況)。當然你也可以l以後的減1,r以後的加1(相當於之前更新過程作廢),這樣也好理解一點。

原始碼:

#include 

#include

#include

#include

#include

#include

#include

#include

using

namespace

std;

const

int maxn = 200000 + 5;

int n, m;

struct d

d[maxn];

bool cmp(d a, d b)

int ans[maxn];

int data[maxn];

int prime[maxn], cnt;

int vis[maxn];

vector

p[maxn];

void init()

}p[0].clear(), p[1].clear();

for(int j = 0 ; j < cnt ; j++)

}int tree[maxn], li[maxn], ri[maxn];

int lowbit(int a)

void updata(int pos, int val)

}int sum(int pos)

return res;

}vector

vc[maxn];

void check_tree()

int main()

int a = vis[mark];

if(ri[a] == 0) ri[a] = i;

if(li[i] < a) li[i] = a;

vis[mark] = i;

}vc[li[i]].push_back(i);

}int cur = 0;

for(int i = 0 ; i <= n ; i++)

if(cur >= m) break;

for(int j = 0 ; j < (int)vc[i].size() ; j++)

if(ri[i] != 0) updata(ri[i], 1);

// printf("check tree\n");

// check_tree();

}for(int i = 0 ; i < m ; i++)

printf("%d\n", ans[i]);

}return

0;}

hdu5775 思維題 樹狀陣列

通過分析可得乙個點它最右邊的位置就是他現在的位置加上他後面的比他小的數字的個數。乙個點最左邊的位置就是他開始的位置或者排完序以後的位置min i,num i 用陣列陣列從後往前可以巧妙的維護乙個點後面符合條件的點有多少個,具體看 include include includeusing namesp...

hdu 3887 樹狀陣列

給你一棵樹,每個節點都有個編號。讓你求乙個節點他的子樹中編號比他小的節點有幾個。編號唯一,從1 n,已給出根節點 解 樹狀陣列統計。轉化為線性序列。可以想到的是,若要統計乙個節點,那麼比它小的孩子必須先插完,然後統計就行了。對於乙個節點i來說,只要把所有x那麼對於所有節點來說也是這樣的,從一開始插,...

hdu 3333 樹狀陣列

此題與3743相仿,但本題資料較大,需要用到離散化。如何去掉重複元素呢?採用離線演算法 首先將詢問按右端點從小到大排序,離線處理時,記錄每個元素所在位置,遇到重複元素時,從它之前出現的位置減去這個元素,這樣就是的每個元素總是出現在最後。include include include include ...