loj3299.「联合省选 2020 A | B」冰火战士

题目链接

90% 的数据是来搞笑吗,真的有人会正解还不会离散化吗。

感觉是非常好的一题,以前树状数组只是背板子,做完这一题后算是真正理解了树状数组。

显然冰系出战的范围是一个前缀,火系出战的是一个后缀,这两个函数一个单调不增,一个单调不降,答案就是两个函数的 \(\min\) 在整点处取到的最大值的两倍。

答案只有可能在两个函数交点的前后取到,一个自然的想法是直接二分出这个极值,配合线段树或树状数组容易做到 \(O(Q\log^2Q)\),可以得到 \(60\) 分的好成绩。

考虑怎样砍掉一只 \(\log\)。不难想到,线段树+二分=线段树二分,而这样就少了一个 \(\log\)。进行两次线段树二分,第一次二分出两函数相交前的最后一个位置,然后下一个位置就是两函数相交后的第一个位置,这样一次二分+两次线段树区间查询得到了两边的最小值的最大值。比较一下,如果是相交前的值更大,就直接输出,否则可能会出现相交后连续一段答案全部相同的情况,而题目要求出温度最大值,于是再进行一次线段树二分二分出来这一段的右端点就是答案。这样时间复杂度降为 \(O(Q\log Q)\),可以通过此题……吗?

很不幸,线段树二分常数太大了,还是过不去。

这样一来我们不能使用线段树,那么只能使用树状数组了,查询很好解决,冰系直接查前缀,火系总和减去前缀即可。然后是一个重要的 trick,我们可以在树状数组上用倍增的方式来实现二分的操作。因为树状数组上编号为 \(i\) 的节点保存的是 \((i-lowbit(i),i]\) 的信息,这样我们从高位向低位枚举,类似求 LCA 的方法,如果满足要求就跳,这样就实现了和二分一样的效果。

但是……每次倍增跳到一个新的节点不是需要查询吗?这样还是 \(O(Q\log^2 Q)\) 的?

于是考虑怎样在倍增的时候 \(O(1)\) 在树状数组上查询前缀和。朴素的查询是单次 \(O(\log Q)\) 的且无法优化,这启示我们必须利用前面倍增得到的信息。还是回到节点 \(i\) 保存的信息的范围是 \((i-lowbit(i),i]\) 上面,我们发现由于每次增加的一定都是当前数位最低的一个 \(1\),换句话说,如果当前跳到的节点是 \(p\),上一个节点是 \(pos\),那么一定有 \(p=pos+lowbit(p)\),前面 \([1,pos]\) 的前缀和已经求出了,后面 \((pos,p]\) 的信息怎么求?对前面的式子移项,得到 \(pos=p-lowbit(p)\) 所以这个区间就是 \((p-lowbit(p),p]\),也就是树状数组上编号为 \(p\) 的节点的值。这样我们就实现了倍增的时候 \(O(1)\) 查询前缀和。

于是树状数组上倍增模拟线段树二分的全部问题都已经解决,直接和线段树二分时进行两次意义完全相同的倍增即可。

时间复杂度依然是 \(O(Q\log Q)\),但树状数组常数就小得多了,可以通过。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,ans[2000001][2],node[2000001],cnt,sum;
struct element
{
    int opt,t,x,y;
}a[2000001];
inline int read()
{
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x;
}
void print(int x)
{
    if(x>=10)
        print(x/10);
    putchar(x%10+'0');
}
inline int lowbit(int x)
{
    return x&-x;
}
inline void update(int x,int val,int tag)
{
    for(;x<=cnt;x+=lowbit(x))
        ans[x][tag]+=val;
}
inline int query(int x,int tag)
{
    int res=0;
    for(;x;x-=lowbit(x))
        res+=ans[x][tag];
    return res;
}
int main()
{
    n=read();
    for(register int i=1;i<=n;++i)
    {
        a[i].opt=read(),a[i].t=read();
        if(a[i].opt==1)
        {
            a[i].x=read(),a[i].y=read();
            node[++cnt]=a[i].x;
        }
    }
    sort(node+1,node+cnt+1);
    cnt=unique(node+1,node+cnt+1)-node-1;
    for(register int i=1;i<=n;++i)
    {
        if(a[i].opt==1)
        {
            a[i].x=lower_bound(node+1,node+cnt+1,a[i].x)-node;
            if(!a[i].t)
                update(a[i].x,a[i].y,0);
            else
            {
                sum+=a[i].y;
                update(a[i].x+1,a[i].y,1);
            }
        }
        else
            if(!a[a[i].t].t)
                update(a[a[i].t].x,-a[a[i].t].y,0);
            else
            {
                sum-=a[a[i].t].y;
                update(a[a[i].t].x+1,-a[a[i].t].y,1);
            }
        int pos=0,ans1=0,ans2=0,tmp1=0,tmp2=sum;
        for(register int j=20;~j;--j)
        {
            if((pos|(1<<j))>cnt)
                continue;
            int p=pos|(1<<j);
            tmp1+=ans[p][0];
            tmp2-=ans[p][1];
            if(tmp1>=tmp2)
            {
                tmp1-=ans[p][0];
                tmp2+=ans[p][1];
                continue;
            }
            ans1=tmp1;
            pos=p;
        }
        if(ans1&&pos==cnt)
        {
            print(node[pos]);
            putchar(' ');
            print(ans1<<1);
            putchar('\n');
            continue;
        }
        ans2=sum-query(pos+1,1);
        if((!ans1&&!ans2)||query(pos+1,0)<ans2)
        {
            puts("Peace");
            continue;
        }
        if(ans1>ans2)
        {
            print(node[pos]);
            putchar(' ');
            print(ans1<<1);
            putchar('\n');
            continue;
        }
        tmp1=0,tmp2=sum,pos=0;
        for(register int j=20;~j;--j)
        {
            if((pos|(1<<j))>cnt)
                continue;
            int p=pos|(1<<j);
            tmp1+=ans[p][0];
            tmp2-=ans[p][1];
            if(tmp1<tmp2||tmp2==ans2)
            {
                pos=p;
                continue;
            }
            tmp1-=ans[p][0];
            tmp2+=ans[p][1];
        }
        print(node[pos]);
        putchar(' ');
        print(ans2<<1);
        putchar('\n');
    }
    return 0;
}
posted @ 2021-08-18 09:26  绝顶我为峰  阅读(44)  评论(0编辑  收藏  举报