【CodeChef AUG16 T4】Chef and His Garden

来源:互联网 发布:win7隐藏网络连接 编辑:程序博客网 时间:2024/05/17 04:56

Score20:
 这种情况很水,枚举每一个区间即可,当然在这个枚举的过程中要直接计算出区间最大值,

 用线段树是过不掉的。。。

Score40(配合上述解法):

 即将题目转化为求1<=i<j<=nmax{Ai,Ai+1,Aj}的值

 那么我们来考虑每个数Ak对答案的贡献

 我们知道当且仅当Ak为区间[i,j]的最大值时,它对答案有贡献

 于是很容易想到的一个思路就是分治

 对于solve(l,r)先找到区间[l,r]中最大值的下标c

 然后累加上它对答案的贡献((cl+1)(rc+1)1)Ac

 这样接下来的所有情况区间都不应包含点c,否则情况重复

 于是我们可以将区间劈成两半,而后分别运行solve(l,c1)solve(c+1,r)

 此情况得解

Score60(配合上述解法):

 我们会发现这时我们可以把对于一个数Ai[0,127]所有与它满足上述条件的数push_backvector

 在c左边计算每个数的cnt,然后在右边扫一遍所有数累加答案就好了

 然而这样每层的是O(n)的,代码总复杂度为O(n227),荣幸的T了。。。。

 然而我们可以发现每次只劈成两个区间

 于是我们可以先解决小的那个区间,然后将其cnt清空,

 然后再解决大的那个区间,不将其cnt清空,用它直接更新这一层的答案

 最后将小区间的cnt补齐,使得每次return到上一层区间时整个小区间的cnt都被累加了

 这样由于每次加减的都是较小的那个区间,复杂度较小

 于是解决了这个情况的问题,复杂度为O(nlogn227)

 那么这个复杂度证明证明呢?

 我们发现,对于每个区间,在它劈成两半时会减去一个点,

 也就是说,每多出一个区间就会少去一个点,

 所以对于每个区间,它的儿子的个数等于区间长度

 所以我没每次保留的那个大区间一定是原区间的重儿子

 所以这个算法的复杂度证明与dsu类似

 可能有重复情况需要特判,这档我没写。。

Score100:
 绕了这么大一个圈,然后我们再来解决这个神奇的原题

 对于100分的情况,我们的眼神必须牢牢盯紧214这个很有猫腻的数字

 经过上一档的解决过程,我们发现,27刚好可以过复杂度

 于是自然而然的想到,将这个214大小的数劈成两半

 那么这时候这么解呢?

 有这样一个思路:我们根据左边的每个数的第一关键字找出他能变成的所有儿子和所有父亲
 如:
 1000001 1011010的第一关键字儿子有

 1000000 1011010
 0000001 1011010
 0000000 1011010
 1000001 1011010这四个数

 我们将这些数全部son_cnt++

 当然对于它的所有第一关键字父亲fa_cnt++

 然后对于右边的每一个数,按照第二关键字走向它的父亲和儿子,累加上所有答案

 比如1000001 1011000这个数

 它可以找到它的第二关键字父亲1000001 1011010,然后答案累加上这个数的son_cnt

 如果是找儿子,那就累加上那个数的fa_cnt

 即如下所示,对于任意两个数,只要存在包含关系,按照这样运动肯定会相撞
  1111111   0110000

  找儿子  找父亲

  0110100   0100000

 当然优化和之前Score 60的写法一样

 值得注意的是,这种情况下左右两区间中有数字相等的话这两个数字的答案就会被重复计算

 于是还要存一个Cnt去掉重复的情况

 于是复杂度O(nlogn227)完美的解决了这个问题

 代码如下:

#include<bits/stdc++.h>using namespace std;#define M 100005#define N 145#define ll long longint a[M],cnt[3][N*N],n;ll ans;vector<int>son[N],fa[N];struct node{    int mx,id;    bool operator <(const node &A)const{        return mx<A.mx;    }};struct Tree{//线段树求取区间最值    node tree[M<<2];    void up(int p){        tree[p]=max(tree[p<<1],tree[p<<1|1]);    }    void build(int p=1,int l=1,int r=n){        if(l==r){            tree[p]=(node){a[l],l};            return;        }        int mid=l+r>>1;        build(p<<1,l,mid);        build(p<<1|1,mid+1,r);        up(p);      }    node query(int L,int R,int p=1,int l=1,int r=n){        if(L==l&&R==r)return tree[p];        int mid=l+r>>1;        if(R<=mid)return query(L,R,p<<1,l,mid);         if(L>mid)return query(L,R,p<<1|1,mid+1,r);        return max(query(L,mid,p<<1,l,mid),query(mid+1,R,p<<1|1,mid+1,r));    }}T;void init__fa_son(){    for(int i=(1<<7)-1;i>=0;i--)        for(int j=i;j>=0;j--)            if((i&j)==j){                son[i].push_back(j);                fa[j].push_back(i);            }}void Up(int x,int y,int f){    for(int i=0;i<son[x].size();i++)cnt[0][(son[x][i]<<7)+y]+=f;//大区间降,小区间升    for(int i=0;i<fa[x].size();i++)cnt[1][(fa[x][i]<<7)+y]+=f;//大区间升,小区间降    cnt[2][(x<<7)+y]+=f;}void Get_ans0(int x,int y,int v){//大区间降,小区间升    for(int i=0;i<fa[y].size();i++)ans+=1ll*cnt[0][(x<<7)+fa[y][i]]*v;}void Get_ans1(int x,int y,int v){//大区间升,小区间降    for(int i=0;i<son[y].size();i++)ans+=1ll*cnt[1][(x<<7)+son[y][i]]*v;}void Get_ans2(int x,int y,int v){//重复    ans-=1ll*cnt[2][(x<<7)+y]*v;}void solve(int l,int r){    if(l>=r){        for(int i=l;i<=r;i++)Up(a[i]>>7,a[i]&((1<<7)-1),1);        return;    }    int c=T.query(l,r).id,mid=l+r>>1;    if(c>mid){//右区间小        solve(c+1,r);//小区间        for(int i=c+1;i<=r;i++)Up(a[i]>>7,a[i]&((1<<7)-1),-1);//删除小区间        solve(l,c-1);//不删除大区间        for(int i=c;i<=r;i++)Get_ans1(a[i]>>7,a[i]&((1<<7)-1),a[c]);//大区间升,小区间降        for(int i=c+1;i<=r;i++)Get_ans2(a[i]>>7,a[i]&((1<<7)-1),a[c]);//重复        Up(a[c]>>7,a[c]&((1<<7)-1),1);        for(int i=c+1;i<=r;i++)Get_ans0(a[i]>>7,a[i]&((1<<7)-1),a[c]);//大区间降,小区间升        for(int i=c+1;i<=r;i++)Up(a[i]>>7,a[i]&((1<<7)-1),1);    }    else {//左区间小        solve(l,c-1);//小区间        for(int i=l;i<c;i++)Up(a[i]>>7,a[i]&((1<<7)-1),-1);//删除小区间        solve(c+1,r);//不删除大区间        for(int i=l;i<=c;i++)Get_ans1(a[i]>>7,a[i]&((1<<7)-1),a[c]);//大区间升,小区间降        for(int i=l;i<c;i++)Get_ans2(a[i]>>7,a[i]&((1<<7)-1),a[c]);//重复        Up(a[c]>>7,a[c]&((1<<7)-1),1);        for(int i=l;i<c;i++)Get_ans0(a[i]>>7,a[i]&((1<<7)-1),a[c]);//大区间降,小区间升        for(int i=l;i<c;i++)Up(a[i]>>7,a[i]&((1<<7)-1),1);    }}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)scanf("%d",&a[i]);    T.build();//建线段树    init__fa_son();//预处理父亲儿子    solve(1,n);//分治    cout<<ans<<'\n';    return 0;}