「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)

  学习了新姿势。。(一直看不懂大爷的代码卡了好久T T

  首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2*s_l-l$的区间$[l+1,r]$其众数为$x$,这个显然可以用一个数据结构来维护。

  直接扫一遍效率是$O($数字种类数$*nlogn)$的,无法承受,但是我们发现,对于每一段非$x$的数,$2*s_i-i$是公差为$-1$的等差数列,所以它们对答案的贡献实际上可以一次性计算。设$L$为一段非$x$数的开头,$R$为结尾,则$\leq 2*s_R-R$的数贡献会被计算$len$次,$2*s_{R-1}-(R-1)$的数的贡献会被计算$len-1$次,...,$s_l-l$的数的贡献会被计算$1$次,这个贡献的计算次数也是个等差数列。

  那实际上我们有三种维护这个的方法。

  ①维护$a_i$表示$2*s_i-i$的出现次数,支持区间加和区间查询$\sum_{i=l}^r a_i*(i-l+1)$,较为麻烦,权值线段树。

  ②维护$s_i$表示$a_i$的前缀和,支持区间加一段等差数列和区间查询,挺可写,权值线段树。

  ③维护$s_i$的前缀和,支持区间加二次函数和单点查询,代码短但因为较抽象所以有些难调,树状数组,非常快。

  这里只说第三种写法,第一次见到这种操作...

  树状数组里实际上维护的是$s_1,s_1+s_2,s_1+s_2+s_3,...$,所以修改一段区间的时候,相当于给一段区间加上等差数列的求和,即$((1+i-l+1)*(i-l+1))/2=(i^2+(3-2l)i+l^2+3l+2)/2$,所以我们只要在树状数组上维护二次项,一次项和常数项,区间修改用差分,最后查询一段区间只要头尾相减就好了...

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=1000010;
int n, x, N, t;
int a[maxn], treety[maxn], pos[maxn], pre[maxn];
ll tree1[maxn], tree2[maxn], tree3[maxn], ans;
char buf[20000010],*ptr=buf-1;
inline int read()
{
    char c=*++ptr;int s=0,t=1;
    while(c<48||c>57)c=*++ptr;
    while(c>=48&&c<=57){s=s*10+c-‘0‘;c=*++ptr;}
    return s*t;
}
inline void add(int x, int p)
{
    x+=n+1;
    ll delta1=1, delta2=3-2*x, delta3=1ll*x*x-3*x+2;
    for(;x<=N;x+=x&-x)
    {
        if(treety[x]!=t) treety[x]=t, tree1[x]=tree2[x]=tree3[x]=0;
        tree1[x]+=delta1*p, tree2[x]+=delta2*p, tree3[x]+=delta3*p;
    }
}
inline void query(int x, int ty)
{
    x+=n+1; int pos=x;
    for(;x;x-=x&-x) if(treety[x]==t)
    ans+=(tree1[x]*pos*pos+tree2[x]*pos+tree3[x])*ty;
}
inline void update(int l, int r, int s){add(2*s-r, 1); add(2*s-l+1, -1);}
int main()
{
    fread(buf,1,sizeof(buf),stdin); n=read(); x=read(); N=n+n+2;
    for(int i=1;i<=n;i++) x=read(), pre[i]=pos[x], pos[x]=i;
    for(int i=0;i<n;i++)
    if(pos[i])
    {
        int cnt=0; ++t;
        for(int j=pos[i];j;j=pre[j]) a[++cnt]=j;
        update(0, a[cnt]-1, 0);
        for(int j=cnt;j;j--)
        {
            query(2*(cnt-j+1)-a[j]-1, 1);
            query(2*(cnt-j+1)-((j-1)?a[j-1]+1:n+2), -1);
            update(a[j], (j-1)?a[j-1]-1:n, cnt-j+1);
        }
    }
    printf("%lld\n", ans>>1);
}

原文地址:https://www.cnblogs.com/Sakits/p/8137882.html

时间: 2024-10-01 06:07:29

「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)的相关文章

[LOJ 6249]「CodePlus 2017 11 月赛」汀博尔

Description 有 n 棵树,初始时每棵树的高度为 H_i,第 i 棵树每月都会长高 A_i.现在有个木料长度总量为 S 的订单,客户要求每块木料的长度不能小于 L,而且木料必须是整棵树(即不能为树的一部分).现在问你最少需要等多少个月才能满足订单. Input 第一行 3 个用空格隔开的非负整数 n,S,L,表示树的数量.订单总量和单块木料长度限制.第二行 n 个用空格隔开的非负整数,依次为 H1,H2,…,Hn.第三行 n 个用空格隔开的非负整数,依次为 A1,A2,…,An. Ou

「CodePlus 2017 11 月赛」汀博尔 (二分答案)

题目链接:https://loj.ac/problem/6249 题意:有 n 棵树,初始时每棵树的高度为 H?i?,第 i 棵树每月都会长高 A?i??.现在有个木料长度总量为 S 的订单,客户要求每块木料的长度不能小于 L,而且木料必须是整棵树(即不能为树的一部分). 现在问你最少需要等多少个月才能满足订单.(数据范围:1<=n<=200000,1<=S,L<=1e18,1<=Hi,Ai<=1e9) 题解:很显然二分答案.上界不能直接选择1e18,会爆long lo

「CodePlus 2017 11 月赛」可做题

这种题显然先二进制拆位,显然改的位置显然只有每一段确定的数的开头和结尾,只需要对于每一个可决策位置都尝试一下填1和0,然后取min即可. #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int maxn=500010;

「CodePlus 2017 12 月赛」火锅盛宴(模拟+树状数组)

1A,拿来练手的好题 用一个优先队列按煮熟时间从小到大排序,被煮熟了就弹出来. 用n个vector维护每种食物的煮熟时间,显然是有序的. 用树状数组维护每种煮熟食物的数量. 每次操作前把优先队列里煮熟时间<=当前时间的弹出,BIT上+1. 每次0操作把食物塞进优先队列和vector 每次1操作先看看树状数组里有没有数,没有输出angry,有的话在树状数组上二分找到最小的数. 每次2操作先看看树状数组里有没有这一种数,有的话输出并-1,没有的话看看vector有没有,有的话输出时间差,没有的话输出

「CodePlus 2017 12 月赛」火锅盛宴

n<=100000种食物,给每个食物煮熟时间,有q<=500000个操作:在某时刻插入某个食物:查询熟食中编号最小的并删除之:查询是否有编号为id的食物,如果有查询是否有编号为id的熟食,如果有熟食删除之,否则输出其离煮熟的最小时间:查询编号在[L,R]的熟食有多少.保证操作时间递增. 可以发现:针对某种食物的信息维护只需要知道其未煮熟的食物中煮熟时间的最小值,而所有的熟食都可以一起维护.为此可以:对每个食物开个队列存未熟食物,对所有食物开个优先队列以便及时把熟食从队列中提出来,并开个以编号为

走进矩阵树定理--「CodePlus 2017 12 月赛」白金元首与独舞

n,m<=200,n*m的方阵,有ULRD表示在这个格子时下一步要走到哪里,有一些待决策的格子用.表示,可以填ULRD任意一个,问有多少种填法使得从每个格子出发都能走出这个方阵,答案取模.保证未确定的格子<=300. ...一脸懵逼地写了原本30实际20的暴力然后跪着啃了下论文 然而什么都没啃懂不如结论记下来: 首先矩阵行列式的定义:一个n*n的矩阵,行列式值为$\sum_{b是n的一个排列} \ \ \ \ \ ( (-1)^{b的逆序对数} \ \ \ \ \ * \prod_{i=1}^

【LIbreOJ】#6256. 「CodePlus 2017 12 月赛」可做题1

[题意]定义一个n阶正方形矩阵为"巧妙的"当且仅当:任意选择其中n个不同行列的数字之和相同. 给定n*m的矩阵,T次询问以(x,y)为左上角的k阶矩阵是否巧妙.n,m<=500,T<=10^5. [算法]数学 [题解] 可以证明每个矩阵是巧妙的当且仅当其每个2阶子矩阵均是巧妙的: 必要性:若该矩阵有一个不巧妙的2阶子矩阵,则其他部分选择相同的情况下(不涉及此两行列),这两行列的和不同,所以该矩阵不是巧妙的. 观察一个巧妙的2阶子矩阵 a1 a2 b1 b2 由a1+b2=a

「CodePlus 2017 12 月赛」白金元首与独舞

description 题面 data range \[ 1 \leq T \leq 10, 1 \leq n, m \leq 200 , 0 \leq k \leq \min(nm, 300)\] solution 矩阵树定理 求无向图的生成树个数 度数矩阵-邻接矩阵 去掉一行一列求行列式 为了保证精度可以辗转相除 这里是模意义下的 const int mod=998244353; int a[305][305]; il int gauss(int n){ RG int ans=1; for(

loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分

$ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinobu 早有准备,Alice.Ayaya.Karen.Shinobu.Yoko 五人又能继续愉快地玩耍啦! 「噢--!不是有放上天的烟花嘛!」Karen 兴奋地喊道. 「啊等等--」Yoko 惊呼.Karen 手持点燃引信的烟花,「嗯??」 Yoko 最希望见到的是排列优美的烟火,当然不会放过这个机会-