Codeforces 475D CGCDSSQ(分治)

题意:给你一个序列a[i],对于每个询问xi,求出有多少个(l,r)对使得gcd(al,al+1...ar)=xi.

表面上是询问,其实只要处理出每个可能的gcd有多少个就好了,当左端点固定的时候,随着右端点的移动,gcd必然是单调非增的,而且个数不会超过log(a[i])个,所以总的不同的个数的上界是nlog(ai),所以求出所有是可行的。

一个分治的做法是这样的,对于一个区间[l,r],分治成[l,mid],[mid+1,r]求解,然后就是合并,合并的时候首先求以[l,mid]右端点为结束点的gcd,然后是[mid+1,r]的左端点为起始点的gcd,两边for一遍,由于不同的gcd最多只有log(ai)个,所以合并的时候就是log(ai)^2。

所以复杂度大致是这样的 T(n)=2*T(n/2)+log(ai)^2+O(n)  所以大致可以看成是T(n)=2*T(n/2)+O(n),所以复杂度大致就是nlogn的级别的。

#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;

#define maxn 110000
#define ll long long
#define MP make_pair

int n;
int a[maxn];
map<int,ll> m;

int gcd(int a,int b){
    return a&&b? gcd(b,a%b):a+b;
}

void solve(int l,int r)
{
    if(r-l<=3){
        for(int i=l;i<=r;++i){
            int g=a[i];
            for(int j=i;j<=r;++j){
                g=gcd(g,a[j]);
                m[g]++;
            }
        }
        return;

    }
    int mid=(l+r)>>1;
    solve(l,mid);
    solve(mid+1,r);
    vector<pair<int,ll> > ls;
    vector<pair<int,ll> > rs;

    int cur=-1;
    ll cnt=0;
    int g=a[mid];
    for(int i=mid;i>=l;--i){
        g=gcd(g,a[i]);
        if(g!=cur) {
            if(cur!=-1) ls.push_back(MP(cur,cnt));
            cur=g;cnt=1;
        }
        else{
            ++cnt;
        }
    }
    ls.push_back(MP(cur,cnt));

    cur=-1;cnt=0;g=a[mid+1];
    for(int i=mid+1;i<=r;++i){
        g=gcd(g,a[i]);
        if(g!=cur) {
            if(cur!=-1) rs.push_back(MP(cur,cnt));
            cur=g;cnt=1;
        }
        else{
            ++cnt;
        }
    }
    rs.push_back(MP(cur,cnt));

    for(int i=0;i<ls.size();++i){
        for(int j=0;j<rs.size();++j){
            int g=gcd(ls[i].first,rs[j].first);
            ll num=ls[i].second*rs[j].second;
            m[g]+=num;
        }
    }
}

int main()
{
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;++i){
            scanf("%d",a+i);
        }
        m.clear();
        solve(1,n);
        int q,xi;
        scanf("%d",&q);
        while(q--){
            scanf("%d",&xi);
            if(m.count(xi)) printf("%I64d\n",m[xi]);
            else puts("0");
        }
    }
    return 0;
}
时间: 2024-10-10 09:44:53

Codeforces 475D CGCDSSQ(分治)的相关文章

Codeforces 475D CGCDSSQ 求序列中连续数字的GCD=K的对数

题目链接:点击打开链接 #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <iostream> #include <cmath> using namespace std; typedef long long ll; template <class T> inline bool rd(T &ret)

CodeForces 475D CGCDSSQ RMQ

通过这道题目还是学到了不少东西的,当时刚拿到这个题目的时候时间已经不多了,因为前面有个C坑到了,看了个大概,然后就往线段树和树状数组方面去想了,对于gcd的区间求一个前缀,再搞一个后缀 瞎弄弄,后来发现错了,题目求的是区间个数...又浪费了一段时间,然后回头再看看,大致就想到了暴力枚举,n是10^5,大不了离线先暴力的高出答案,想到一般发现  若假定一个询问输入的数 为  X,那么另一个 能够 y 通过gcd(x,y)得到的另一个数的 答案就是它自身的答案数再加上当前x的答案数,这样就有递推了,

Codeforces 888G Xor-MST - 分治 - 贪心 - Trie

题目传送门 这是一条通往vjudge的高速公路 这是一条通往Codeforces的高速公路 题目大意 给定一个$n$阶完全图,每个点有一个权值$a_{i}$,边$(i, j)$的权值是$(a_{i}\  xor\  a_{j})$.一个生成树的权值是各边的权值和.问最小生成树的权值. 设值域为$[0, 2^{k})$. 当$k = 1$的时候,显然将点权为0的点之间互相连边,点权为1的点之间互相连边,中间随便连一条. 当$k = x\ (x > 1)$的时候,将这些点按照二进制的第$k$位分成两

codeforces 475D

题意:给定n(n<=100000)个1e9以内的数的数组a,然后最多有3*1e5的询问,对于每个询问,给定一个x,问有多少个(l<=r&&gcd(a[l],a[l+1]...a[r]) == x) 思路:昨天的比赛题..可惜我被c题wa到放弃了这场比赛..也就没看了..不妨设G(l,r) = gcd(a[l], a[l+1]...a[r]); 其实题目最关键的的性质是对于G(l,r),G(l,r+1)后者肯定比前者更小.. 所以就可以暴力了..从后往前扫描i,处理(i, n)这

Codeforces 161C(分治、性质)

要点 因为当前最大字符只有一个且两边是回文的,所以如果答案包含最大字符则一定是重合部分. 若不包含,则用此字符将两个区间分别断为两部分,则共有四种组合,答案一定为其中之一. #include <cstdio> #include <algorithm> using namespace std; int l1, l2, r1, r2; int Divide(int a, int b, int c, int d, int depth) { if (a > b || c > d

Codeforces Round #256 (Div. 2/C)/Codeforces448C_Painting Fence(分治)

解题报告 给篱笆上色,要求步骤最少,篱笆怎么上色应该懂吧,,,刷子可以在横着和竖着刷,不能跳着刷,,, 如果是竖着刷,应当是篱笆的条数,横着刷的话,就是刷完最短木板的长度,再接着考虑没有刷的木板,,, 递归调用,,, #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define inf 999999999999999 using namespace

codeforces 448C C. Painting Fence(分治+dp)

题目链接: codeforces 448C 题目大意: 给出n个杆子,每个杆子有一个长度,每次可以刷一行或一列,问最少刷多少次可以将整个墙刷成黄色. 题目分析: 首先我们能够想到,如果横着刷,为了得到最优解,当前刷的位置的下面也必须横着刷,然后对于每种情况都可以通过n次竖着刷得到整个黄色的墙. 所以我们采取分治的策略进行动态规划,也就是对于每个状态划分为两种情况讨论,如果要刷横向的话,最矮要刷到最矮的柱子的高度才可能得到比竖着刷优的解,然后就变成了多个具有相同性质的规模更小的墙,然后我们可以采取

Codeforces 448C Painting Fence:分治

题目链接:http://codeforces.com/problemset/problem/448/C 题意: 有n个木板竖着插成一排栅栏,第i块木板高度为a[i]. 你现在要将栅栏上所有地方刷上油漆. 每次你可以选择竖着刷或横着刷,但必须保证一次刷的地方不能间断. 问你至少要刷几次才能刷满. 题解: 首先有一个贪心结论: 对于当前要刷的一片区域,令minn为这片区域的最小高度. 如果选择横着刷,则至少要将区域底部的minn层刷完. 如图,至少要将下面两层刷完: 然后考虑如何分治: 对于当前的这

CodeForces 958F3 Lightsabers (hard) 启发式合并/分治 多项式 FFT

原文链接http://www.cnblogs.com/zhouzhendong/p/8835443.html 题目传送门 - CodeForces 958F3 题意 有$n$个球,球有$m$种颜色,分别编号为$1\cdots m$,现在让你从中拿$k$个球,问拿到的球的颜色所构成的可重集合有多少种不同的可能. 注意同种颜色球是等价的,但是两个颜色为$x$的球不等价于一个. $1\leq n\leq 2\times 10^5,\ \ \ \ \ 1\leq m,k\leq n$. 题解 来自Hel