HDU 5869.Different GCD Subarray Query-区间gcd+树状数组 (神奇的标记右移操作) (2016年ICPC大连网络赛)...
樹狀數組。。。
Different GCD Subarray Query
Time Limit: 6000/3000 MS (Java/Others)????Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1541????Accepted Submission(s): 599
??
??Given an array?a?of?N?positive integers?a1,a2,?aN?1,aN; a subarray of?a?is defined as a continuous interval between?a1?and?aN. In other words,?ai,ai+1,?,aj?1,aj?is a subarray of?a, for?1≤i≤j≤N. For a query in the form?(L,R), tell the number of different GCDs contributed by all subarrays of the interval?[L,R].
??
?
Input There are several tests, process till the end of input.??
??For each test, the first line consists of two integers?N?and?Q, denoting the length of the array and the number of queries, respectively.?N?positive integers are listed in the second line, followed by?Q?lines each containing two integers?L,R?for a query.
You can assume that?
??
????1≤N,Q≤100000?
????
???1≤ai≤1000000
?
Output For each query, output the answer in one line.?
Sample Input 5 3 1 3 4 6 9 3 5 2 5 1 5?
Sample Output 6 6 6?
Source 2016 ACM/ICPC Asia Regional Dalian Online?
?
這個題的意思就是問區間內所有子段不同gcd的個數。
一開始理解錯了題意,后來想明白了。假設區間的數為1 2 3 4
就是gcd(1),gcd(2),gcd(3),gcd(4),gcd(1,2),gcd(2,3),gcd(3,4),gcd(1,2,3),gcd(2,3,4),gcd(1,2,3,4)這些里面不同的gcd的個數是多少。
如果用在線算法操作,每查詢一次就要處理依次數據,一方面會造成浪費,另一方面,你這樣寫妥妥的超時啊,所以要用離線算法,將所有的數據處理好之后,按順序直接輸出結果就可以。
首先用一個數組將數據保存起來,然后用一個vector數組將查詢的區間和查詢的順序記錄下來。
處理數據,就是相對來說固定右端點,從右往左移,在代碼里就是對于每一個i(固定右端點),遍歷一下i所在的子段的gcd,因為i不變,j是上一個i的gcd的值保存的順序,j越大,i所在的子段就依次向左增加一個數,如果gcd發生了變化,就記錄一下這個gcd以及出現的位置。可能越說越亂,畫一個圖表示一下。。。
圖的意思就是假設數據為2 4 9 6 5,i為下標。假設i為4,就是固定4,然后遍歷j,j存的是上一個i的子段的gcd,通過上一個i的gcd來得到i為4時(6)的子段的gcd,圖中畫的橫線的長度就是子段的長度,橙色的矩形包含的長度是上一個i處理出的數據。就是固定右端點,依次往左遍歷得到所有的gcd,這樣不會重復操作,也不會漏掉某個子段。
將gcd整理出來之后,怎么操作才能使得區間查詢的是不同的gcd的個數呢?
對于區間內相同的gcd,讓標記gcd的下標盡量右移,找該gcd的最大右端點。
通過樹狀數組維護gcd的下標。
一邊維護,一邊查詢樹狀數組就可以保證數據正確。
總結一下就是:固定右端點,依次往左找出來所有的gcd,并標記下標,因為是i逐漸增大的,所以后面遍歷的時候也是相同gcd的最大右端點。直接查詢就可以。
就這樣吧,不想好好寫了。
?
代碼:
1 //離線處理-樹狀數組 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #include<cstdlib> 7 #include<algorithm> 8 #include<queue> 9 #include<vector> 10 #include<utility> 11 using namespace std; 12 const int maxn=1e5+10; 13 int Arr[maxn];//存數據一開始的值 14 int N,Q; 15 int pos[maxn*10]; //記錄gcd的位置 16 vector<pair<int ,int> >querys[maxn]; 17 vector<pair<int, int> >gcds[maxn]; 18 int ans[maxn]; 19 int treeArr[maxn]; //樹狀數組 20 21 int gcd(int a,int b){ //gcd求最大公約數 22 if(!(a%b))return b; 23 else return gcd(b,a%b); 24 } 25 26 void init(){ //初始化 27 int tmp,ts; 28 for(int i=1;i<=N;i++){ 29 tmp=Arr[i]; 30 ts=i; //固定右端點,向左遍歷 31 for(int j=0;j<gcds[i-1].size();j++){ 32 int tmpgcd=gcd(tmp,gcds[i-1][j].first); 33 if(tmpgcd!=tmp){ //如果gcd發生變化 34 gcds[i].push_back(make_pair(tmp,ts)); //first存gcd,second存gcd的左端點ts 35 ts=gcds[i-1][j].second; //上一個gcd的右端點就是下一個gcd的左端點 36 tmp=tmpgcd; 37 } 38 } 39 gcds[i].push_back(make_pair(tmp,ts)); //將與上一個的最左端的gcd存起來 40 } 41 return; 42 } 43 44 int lowbit(int x){ //取最低位的1 45 return x&(-x); 46 } 47 48 void Add(int cur,int num){ //單點更新 49 for(int i=cur;i<=N;i+=lowbit(i)) 50 treeArr[i]+=num; //由葉子節點向上更新樹狀數組 51 return; 52 } 53 54 int Query(int cur){ //區間查詢 從右端點往左加二進制最低位1的 55 int sum=0; 56 for(int i=cur;i>0;i-=lowbit(i)) 57 sum+=treeArr[i]; 58 return sum; 59 } 60 61 void Solve(){ 62 memset(pos,0,sizeof(pos)); 63 memset(treeArr,0,sizeof(treeArr)); 64 for(int i=1;i<=N;i++){ 65 for(int j=0;j<gcds[i].size();j++){ 66 if(pos[gcds[i][j].first]){ //如果標記已經存在,就將標記去掉,所以執行單點更新操作 67 Add(pos[gcds[i][j].first],-1); 68 } 69 Add(gcds[i][j].second,1);//一個新的不同的gcd,從葉子節點開始更新個數+1 70 pos[gcds[i][j].first]=gcds[i][j].second;//記錄該gcd的右端點 71 } 72 for(int j=0;j<querys[i].size();j++){ 73 ans[querys[i][j].second]=Query(i)-Query(querys[i][j].first-1);//區間右端點-區間左區間 74 } 75 } 76 for(int i=1;i<=Q;i++) 77 printf("%d\n",ans[i]); 78 79 return; 80 } 81 int main(){ 82 //reopen("input","r",stdin); 83 while(~scanf("%d%d",&N,&Q)){ 84 for(int i=1;i<=N;i++){ 85 scanf("%d",&Arr[i]); //將數據存在Arr數組中 86 querys[i].clear(); //清空 87 gcds[i].clear(); //清空 88 } 89 init(); 90 int tmp1,tmp2; 91 for(int i=1;i<=Q;i++){ 92 scanf("%d%d",&tmp1,&tmp2); 93 querys[tmp2].push_back(make_pair(tmp1,i)); //將tmp1與i成對插入vector的tmp2下標里 94 } 95 Solve(); 96 } 97 return 0; 98 }?
?
就這樣吧,這題沒什么,主要是想明白就很簡單。
咸魚太菜,學長要捶爆我的狗頭嗎???
已經做好了被學長打死的思想準備。。。
?
轉載于:https://www.cnblogs.com/ZERO-/p/8672921.html
總結
以上是生活随笔為你收集整理的HDU 5869.Different GCD Subarray Query-区间gcd+树状数组 (神奇的标记右移操作) (2016年ICPC大连网络赛)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一天学习一个设计模式之命令模式
- 下一篇: Visual Studio 2015 自