2019徐州网络赛

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
",item.first
56 //        ,item.second);
57 //    puts("");
58     for(auto item : gcdp){
59         int tmp = item.second;
60 //        printf("second : %d
",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
",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
",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
",ans[j]);
92     }
93     //__clock_t edt = clock();
94 //    printf("Used Time : %.3lfms
", static_cast<double>(edt - stt)/1000.0);
95     return 0;
96 }
View Code

 开始gcdp的数组大小开少了会RE,后来改用vector ;最初没有记录上一次 ind 的位置导致重复-1,样例太水,还是自己构造的数据发现的。

如果是解多维或者其他大小的偏序关系区间查询问题,如果查询没有强制在线,应该都可以转化成这种离线存查询排序,以最后一维度值做树状数组的查询与修改。而这种做法需要小心每个点的修改次数。

ps:此题还有CDQ与线段树做法。

原文地址:https://www.cnblogs.com/Kiritsugu/p/11493247.html