I 题 query 题目链接
题目大意是给一个N(<=1e5) permutation p (下标从1开始) , 现在定义一种pair( i ,j) ,其满足 min(pi?,pj?)=gcd(pi?,pj?),现在有M (<=1e5) 组区间查询[l,r]询问 满足 l <= i < j <= r 的pair有多少对。
这是一种偏序关系的问题,看了一点CDQ 解偏序的介绍 , 感觉也可以树状数组来搞。
预处理出每个符合要求的点对( l , r )位置,在一维树状数组上修改点对右端位置 r 的值(+1),这些点对排序好后放在gcdp(这些点对的数量是 NlogN级别的)。
然后离线做,存查询对,并排序。按顺序回答查询,如果查询为 [L,R] 就可以二分查找[L,L] 在 gcdp里的位置 ind,然后将gcdp里 ind 位置前 所有的点对 对树状数组的修改还原(-1),再通过树状数组查询 R 的前缀和 ,即是当前查询的答案。(因为这样可以满足树状数组里 [1,R]全部都是 l >= L 的点对施加的影响 , l < L 的点对影响必然在ind位置之前所以被还原了),这是看CDQ偏序得到的想法 。
因为查询是排好序的,所以每一个点对(l , r)给树状数组的影响最多只有两次(+1 与 -1),只要记录上一次ind的位置就可以不重复-1,最坏复杂度 O(2*N*logN*logN + M * log(N*logN) )
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef pair<int,int> pii; 5 const int maxn = 1e5; 6 int n,m; 7 int raw[maxn + 10],ind[maxn + 10]; 8 vector<pii> gcdp; 9 pii query[maxn+10]; 10 int queryp[maxn + 10]; 11 int ans[maxn + 10]; 12 int Flick[maxn + 10]; 13 14 bool cmp(int i,int j){ 15 return query[i] < query[j]; 16 } 17 18 int lowbit(int x){ 19 return x&(-x); 20 } 21 22 void FlickUpdate(int ind,int val){ 23 while(ind <= n){ 24 Flick[ind] += val; 25 ind += lowbit(ind); 26 } 27 } 28 29 int FlickQuery(int ind){ 30 int ret = 0; 31 while(ind > 0){ 32 ret += Flick[ind]; 33 ind -= lowbit(ind); 34 } 35 return ret; 36 } 37 38 int main(){ 39 //__clock_t stt = clock(); 40 scanf("%d%d",&n,&m); 41 for(int i=1;i<=n;++i){ 42 scanf("%d",&raw[i]); 43 ind[raw[i]] = i; 44 } 45 // int tot = 0; 46 for(int i=1;i<=n;++i){ 47 for(int j = 2 * i;j<=n; j+= i){ 48 int L = ind[i] , R = ind[j]; 49 if(L >= R) swap(L,R); 50 gcdp.emplace_back(make_pair(L,R)); 51 } 52 } 53 sort(gcdp.begin(),gcdp.end()); 54 // puts("gcdmin pair :"); 55 // for(auto item : gcdp) printf("%d %d\n",item.first 56 // ,item.second); 57 // puts(""); 58 for(auto item : gcdp){ 59 int tmp = item.second; 60 // printf("second : %d\n",tmp); 61 FlickUpdate(tmp,1); 62 } 63 64 for (int j = 1; j <= m; ++j) { 65 int l,r; 66 scanf("%d%d",&l,&r); 67 query[j] = make_pair(l,r); 68 queryp[j] = j; 69 } 70 sort(queryp + 1 ,queryp + 1 + m,cmp); 71 // puts("query pair :"); 72 // for(int j = 1;j<=m;++j) printf("%d %d\n",query[queryp[j]].first 73 // ,query[queryp[j]].second); 74 // puts(""); 75 int last = 0; 76 for(int j=1;j<=m;++j){ 77 int numq = queryp[j]; 78 pii qry = query[numq]; 79 80 pii change = make_pair(qry.first,qry.first); 81 int bound = lower_bound(gcdp.begin(),gcdp.end(),change) - gcdp.begin(); 82 // printf("[%d,%d] : %d %d\n",qry.first,qry.second,last,bound); 83 for(int k = last;k<bound ;++k){ 84 FlickUpdate(gcdp[k].second,-1); 85 } 86 last = max(bound,last); 87 88 ans[numq] = FlickQuery(qry.second); 89 } 90 for(int j=1;j<=m;++j){ 91 printf("%d\n",ans[j]); 92 } 93 //__clock_t edt = clock(); 94 // printf("Used Time : %.3lfms\n", static_cast<double>(edt - stt)/1000.0); 95 return 0; 96 }
开始gcdp的数组大小开少了会RE,后来改用vector ;最初没有记录上一次 ind 的位置导致重复-1,样例太水,还是自己构造的数据发现的。
如果是解多维或者其他大小的偏序关系区间查询问题,如果查询没有强制在线,应该都可以转化成这种离线存查询排序,以最后一维度值做树状数组的查询与修改。而这种做法需要小心每个点的修改次数。
ps:此题还有CDQ与线段树做法。
原文地址:https://www.cnblogs.com/Kiritsugu/p/11493247.html