题意
度度熊有一个长度为 N 的数组 A,和一个整数 K。
有正好 N 次操作,每次操作会删除一个位置(该位置将永久失效)。
在每次操作之前,度度想知道,对于所有不包含失效位置的非空区间,权值和最接近 K 的是哪个。
即每次你要找到一个非空区间
,满足对于任何 i≤t≤j 的 t,位置 t 还没有被删除过。同时,你要使这个区间的的权值和最接近 K。
请输出该区间权值和与 K 的差值的绝对值。
前言
比赛的时候打错了一个字符,其实是没有注意到负数,然后过了对拍无限GG,心态崩了
晚上更是想偏了。。还好及时去颓了,没有浪费时间
今早冷静分析了一下,发现自己一开始是对的,然后发现了傻逼错误改了就A了
心态又崩了
题解
考虑倒着做,每一次,一个点变得可以用了
就相当于是两个区间可以合并在一起、
如果可以快速得到,来自新区间的答案,也就是跨过了中界线的答案,就可以维护了
肯定是要统计一个前缀和
然后考虑启发式合并
暴力枚举小的哪一个的所有元素
设区间为的值
,a,b都代表前缀和
那么,如果我们枚举的是b
我们有
把绝对值拆开讨论一下
如果答案是
那么要求
然后再另外一个地方找比(k+b)小的最大值就可以了
另外一种是
,找比
大的最小值就可以了
如果我们枚举的是a
那么有
得到
找比
大的最小值就可以了
找比
小的最大值就可以了
每一个联通快维护两个set,一个是当a的时候使用的,一个是当b的时候使用的
因为前缀和得到区间的形式是
因此,当
的时候是
,当b的时候是
然后启发式合并就可以了
时间复杂度是
代码其实不难写,就是考场太紧张了
CODE:
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
typedef long long LL;
LL MAX=(1LL<<55);
const LL N=100005;
set<LL >::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<LL> 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;
}