百度之星初赛E hdu 6378 度度熊玩数组

题意

度度熊有一个长度为 N 的数组 A,和一个整数 K。
有正好 N 次操作,每次操作会删除一个位置(该位置将永久失效)。
在每次操作之前,度度想知道,对于所有不包含失效位置的非空区间,权值和最接近 K 的是哪个。
即每次你要找到一个非空区间 [i,j](1ijN) [ i , j ] ( 1 ≤ i ≤ j ≤ N ) ,满足对于任何 i≤t≤j 的 t,位置 t 还没有被删除过。同时,你要使这个区间的的权值和最接近 K。
请输出该区间权值和与 K 的差值的绝对值。

前言

比赛的时候打错了一个字符,其实是没有注意到负数,然后过了对拍无限GG,心态崩了
晚上更是想偏了。。还好及时去颓了,没有浪费时间
今早冷静分析了一下,发现自己一开始是对的,然后发现了傻逼错误改了就A了
心态又崩了

题解

考虑倒着做,每一次,一个点变得可以用了
就相当于是两个区间可以合并在一起、
如果可以快速得到,来自新区间的答案,也就是跨过了中界线的答案,就可以维护了
肯定是要统计一个前缀和
然后考虑启发式合并
暴力枚举小的哪一个的所有元素
设区间为的值 (ab) ( a − b ) ,a,b都代表前缀和
那么,如果我们枚举的是b
我们有 |k(ab)| | k − ( a − b ) |
把绝对值拆开讨论一下
如果答案是 k(ab)=ka+b k − ( a − b ) = k − a + b
那么要求 (k+b)a0 ( k + b ) − a ≥ 0
然后再另外一个地方找比(k+b)小的最大值就可以了
另外一种是 (ab)k=a(b+k) ( a − b ) − k = a − ( b + k ) ,找比 (b+k) ( b + k ) 大的最小值就可以了
如果我们枚举的是a
那么有 ka+b=(ka)+b>0 k − a + b = ( k − a ) + b > 0 得到 b>(ka) b > − ( k − a )
找比 (ka) − ( k − a ) 大的最小值就可以了
abk=(ak)b>0 a − b − k = ( a − k ) − b > 0 找比 (ak) ( a − k ) 小的最大值就可以了
每一个联通快维护两个set,一个是当a的时候使用的,一个是当b的时候使用的
因为前缀和得到区间的形式是 sum[r]sum[l1] s u m [ r ] − s u m [ l − 1 ]
因此,当 a a 的时候是 sum[x] s u m [ x ] ,当b的时候是 sum[x1] s u m [ x − 1 ]
然后启发式合并就可以了
时间复杂度是 O(nlog2n) O ( n l o g 2 n )
代码其实不难写,就是考场太紧张了
CODE:

#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
LL MAX=(1LL<<55);
const LL N=100005;
set::iterator it,it1;
LL n,k;
LL a[N],ss[N],f[N];
LL tot[N];
LL find_fa (LL x)    {return f[x]==x?f[x]:f[x]=find_fa(f[x]);}
set s[N],s1[N];
LL ans;
void Merge (LL x,LL y)//这两个联通块可以合在一起了
{
    bool tf=false;//前面的小
    if (tot[x]>tot[y])
    {
        tf=true;
        swap(x,y);//把x并在y那里    
    }
    //printf("YES:%I64d %I64d %d\n",x,y,tf);
    f[x]=y;tot[y]=tot[y]+tot[x];
    if (tf==false)
    {
        for (it=s1[x].begin();it!=s1[x].end();it++)
        {
            LL xx=(*it);
            LL t=xx+k;
            it1=s[y].lower_bound(t);
            if (it1!=s[y].end()) ans=min(ans,abs(*(it1)-t));
            if (it1!=s[y].begin())    
            {
                it1--;
                ans=min(ans,abs(*(it1)-t));
            }
        }
    }
    else
    {
        for (it=s[x].begin();it!=s[x].end();it++)
        {
            LL xx=(*it);
            LL t=xx-k;
            it1=s1[y].lower_bound(t);
            if (it1!=s1[y].begin())
            {
                it1--;
                ans=min(ans,abs((*it1)-t));
            }
            t=k-xx;
            it1=s1[y].lower_bound(-t);
            if (it1!=s1[y].end())    ans=min(ans,abs((*it1)+t));
        }
    }
//    printf("TYB\n");
    for (it=s[x].begin();it!=s[x].end();it++)    s[y].insert((*it));
    for (it=s1[x].begin();it!=s1[x].end();it++)    s1[y].insert((*it));
    s[x].clear();s1[x].clear();
}
LL Ans[N];
int main()
{
    a[0]=0;
    while (scanf("%I64d%I64d",&n,&k)!=EOF)
    {
        memset(f,0,sizeof(f));
        memset(tot,0,sizeof(tot));
        for (LL u=1;u<=n;u++)    scanf("%I64d",&a[u]);
        for (LL u=1;u<=n;u++) a[u]=a[u]+a[u-1];
        for (LL u=1;u<=n;u++)     scanf("%I64d",&ss[u]);
        bool ok=false;
        for (LL u=n;u>=1;u--)
        {
            LL x=ss[u];
            //printf("del:%I64d %I64d\n",x,a[x]);
            f[x]=x;tot[x]=1;
            s[x].clear();s1[x].clear();
            s[x].insert(a[x]);
            s1[x].insert(a[x-1]);
            //printf("add:%I64d %I64d\n",a[x],a[x-1]);
            if (ok==false) {ans=abs((a[x]-a[x-1])-k);ok=true;}
            ans=min(ans,abs((a[x]-a[x-1])-k));
            if (f[x+1]!=0)    Merge(find_fa(x),find_fa(x+1));
            if (f[x-1]!=0)    Merge(find_fa(x-1),find_fa(x));
            Ans[u]=ans;
            /*printf("%d\n",ans);
            system("pause");*/
        }
        for (LL u=1;u<=n;u++)    printf("%I64d\n",Ans[u]);
    }
    return 0;
}

你可能感兴趣的:(不想分类的)