codeforces 671C Ultimate Weirdness of an Array 线段树+构造

题解上说的很清楚了,我照着写的,表示膜拜题解

然后时间复杂度我觉得应该是O(nlogn),虽然常数略大,预处理和倒着扫,都是O(nlogn)

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 2e5+5;
int mat[N],n,mx,tmp,lz[N<<2],a[N<<2];
LL c[N<<2],sum,h[N];
vector<int>v[N];
void build(int rt,int l,int r){
    if(l==r){
        a[rt]=c[rt]=1ll*l;
        return;
    }
    int m=(l+r)>>1;
    build(rt<<1,l,m);
    build(rt<<1|1,m+1,r);
    c[rt]=c[rt<<1]+c[rt<<1|1];
    a[rt]=min(a[rt<<1],a[rt<<1|1]);
}
void down(int rt,int l,int r){
   if(!lz[rt])return;
   int m=(l+r)>>1;
   c[rt<<1]=1ll*lz[rt]*(m-l+1);
   c[rt<<1|1]=1ll*lz[rt]*(r-m);
   a[rt<<1]=a[rt<<1|1]=a[rt];
   lz[rt<<1]=lz[rt<<1|1]=lz[rt];
   lz[rt]=0;
}
void modify(int rt,int l,int r,int x,int y){
   if(x<=l&&r<=y){
     c[rt]=1ll*tmp*(r-l+1);
     a[rt]=lz[rt]=tmp;
     return;
   }
   down(rt,l,r);
   int m=(l+r)>>1;
   if(x<=m)modify(rt<<1,l,m,x,y);
   if(y>m)modify(rt<<1|1,m+1,r,x,y);
   c[rt]=c[rt<<1]+c[rt<<1|1];
   a[rt]=min(a[rt<<1],a[rt<<1|1]);
}
int ask(int rt,int l,int r,int k){
    if(l==r)return l;
    int m=(l+r)>>1;
    if(a[rt<<1|1]<k)ask(rt<<1|1,m+1,r,k);
    else ask(rt<<1,l,m,k);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
      int x;scanf("%d",&x);
      mat[x]=i;mx=max(mx,x);
    }
    for(int i=1;i<=mx;++i){
      for(int j=i;j<=mx;j+=i)
        if(mat[j])
        v[i].push_back(mat[j]);
    }
    for(int i=1;i<=mx;++i)
    if(v[i].size())sort(v[i].begin(),v[i].end());
    build(1,1,n);
    sum=1ll*n*(n+1);
    for(int i=mx;i>=0;--i){
      h[i]=sum-c[1];

      int k=v[i].size();
      if(k<2)continue;
      tmp=n+1;
      if(v[i][1]+1<=n){
          modify(1,1,n,v[i][1]+1,n);
      }
      tmp=v[i][k-1];
      if(a[1]<tmp){
        int pos=ask(1,1,n,tmp);
        pos=min(pos,v[i][1]);
        if(pos>v[i][0])
        modify(1,1,n,v[i][0]+1,pos);
      }
      tmp=v[i][k-2];
      if(a[1]<tmp){
        int pos=ask(1,1,n,tmp);
        pos=min(pos,v[i][0]);
        if(pos>0)
        modify(1,1,n,1,pos);
      }
    }
    LL ans=0;
    for(int i=1;i<=mx;++i)
        ans+=1ll*i*(h[i]-h[i-1]);
    printf("%I64d\n",ans);
    return 0;
}

时间: 2024-08-30 15:00:00

codeforces 671C Ultimate Weirdness of an Array 线段树+构造的相关文章

CodeForces 52C Circular RMQ(区间循环线段树,区间更新,区间求和)

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://codeforces.com/problemset/problem/52/C You are given circular array a0,?a1,?...,?an?-?1. There are two types of operations with it: inc(lf,?rg,?v) - this operation increases each element on the segm

codeforces 446C DZY Loves Fibonacci Numbers 数论+线段树成段更新

DZY Loves Fibonacci Numbers Time Limit:4000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Appoint description:  System Crawler  (2014-07-14) Description In mathematical terms, the sequence Fn of Fibonacci numbers is defi

Codeforces Beta Round #12 D. Ball (线段树)

题目大意: n个女性中,如果有一个女性的三维比这个女性的三维都大,这个女性就要自杀.问要自杀多少个. 思路分析: 先按照第一维排序. 然后离散化第二维,用第二维的下标建树,树上的值是第三维,更新的时候取最大值. 注意是按照第一维度从大到小进入线段树. 而且还要严格递增. 所以处理第一维度比较大小的时候要分开处理,要把相同的先判断,再更新入树. 那么如何判断这个女性是否自杀. 首先,你知道第一维度是从大到小的,所以先进树了的节点的第一维度一定更大. 再次,我们考虑第二维度,我们去树上第二维度下标大

Codeforces 482B Interesting Array(线段树)

题目链接:Codeforces 482B Interesting Array 题目大意:给定一个长度为N的数组,现在有M个限制,每个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是 否有满足的数列. 解题思路:线段树维护,每条限制等于是对l~r之间的数或上q(取且的性质,相应二进制位一定为1),那么处理完所有的 限制,在进行查询,查询对应每个l~r之间的数取且是否还等于q.所以用线段树维护取且和,修改为或操作. #include <cstdio> #include <c

codeforces CF718C Sasha and Array 线段树维护矩阵

$ \Rightarrow $ 戳我进CF原题 C. Underground Lab time limit per test: 1 second memory limit per test: 256 megabytes input: standard input output: standard output The evil Bumbershoot corporation produces clones for gruesome experiments in a vast undergroun

Codeforces Round #275 Div.1 B Interesting Array --线段树

题意: 构造一个序列,满足m个形如:[l,r,c] 的条件. [l,r,c]表示[l,r]中的元素按位与(&)的和为c. 解法: 线段树维护,sum[rt]表示要满足到现在为止的条件时该子树的按位与和至少为多少. 更新时,如果val的pos位为1,那么整个区间的按位与和pos位也应该为1,否则与出来就不对了.(这是本题解题的核心) 那么此时更新 sum[rt] |= val 即可.然后再check一遍看是否满足所有条件即可. 代码: #include <iostream> #inclu

Educational Codeforces Round 37-F.SUM and REPLACE (线段树,线性筛,收敛函数)

F. SUM and REPLACE time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard output Let D(x) be the number of positive divisors of a positive integer x. For example, D(2)?=?2 (2 is divisible by 1 and 2), D(6)?

Educational Codeforces Round 23 F. MEX Queries(线段树)

题目链接:Educational Codeforces Round 23 F. MEX Queries 题意: 一共有n个操作. 1.  将[l,r]区间的数标记为1. 2.  将[l,r]区间的数标记为0. 3.  将[l,r]区间取反. 对每个操作,输出标记为0的最小正整数. 题解: hash后,用线段树xjb标记一下就行了. 1 #include<bits/stdc++.h> 2 #define ls l,m,rt<<1 3 #define rs m+1,r,rt<&l

Codeforces 484E Sign on Fence(可持久化线段树+二分)

题目链接:Codeforces 484E Sign on Fence 题目大意:给定给一个序列,每个位置有一个值,表示高度,现在有若干查询,每次查询l,r,w,表示在区间l,r中, 连续最长长度大于w的最大高度为多少. 解题思路:可持久化线段树维护区间合并,前端时间碰到一题可持久化字典树,就去查了一下相关论文,大概知道了是 什么东西. 将高度按照从大到小的顺序排序,然后每次插入一个位置,线段树维护最长连续区间,因为插入是按照从大到小的顺 序,所以每次的线段树中的连续最大长度都是满足高度大于等于当