Description
Input
Output
Sample Input
10 7
0 3 1 4 6 2 7 8 10 1
2 5
1 3
9 36
4 10
4 9
1 2
1 0
Sample Output
1
-1
9
-1
4
-1
1
Data Constraint
思路
数据结构毒瘤题
题意是1~n个非负整数有m次修改,每次修改后求出一个最小的位置t,使得sum[1~t-1]=p[t]。
假设当前修改的位置x在上次得出的答案位置的后面,那么答案不变,对吧?
那么假设位置x在上次得出的答案位置的前面,这次的答案就要重新计算了。 此时这次答案若存在,则一定在x~n范围内。
我们在x~n范围内找到一个最靠前的位置t,使得t上面的值要大于等于sum[1~x-1]。因为如果t要是史前最大毒瘤,则p[t]是要等于sum[1~t-1],所以p[t]一定要满足大于等于sum[1~x-1]。(这里之所以有等于是因为t有可能就在位置x)
找到了t以后,我们判断t是否是史前最大毒瘤(即t是否等于sum[1~t-1]),如果是,就是答案了,如果不是,则答案区间变成了t+1~n,我们再找下一个t‘
t’要满足大于sum[1~t]的且最靠前的。(之所以不让t’一次性就大于等于sum[1~t’-1]是因为这样真的做不到)
然后我们把找出的t’判断是否是答案,如果是就输出,如果不是就继续找下一个t”,以此类推。
算一下时间复杂度。
每次找t可以用logn(我不会,想知道的可以去问问其他过的dalao)或者logn^2的时间(二分套线段树)。
至于有多少个t嘛,最多只可能有log(10^9)个,为什么呢?因为是类似前缀和的,所以每个sum[1~t]相当于至少是前面sum[1~t-1]的2倍。至于p有0的情况嘛,讲题的时候我被一群dalao带偏了,其实现在想想有很多0也没关系啊,t肯定不会在0上,那不是直接跳过的吗。 如果全部都是0,那答案是1直接稍微判断一下使t不会一个一个往下枚举就好了。
所以,综上,时间复杂度是O(能过)呸应该是O(mlog^2)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e6+777;
int tr[maxn],a[maxn],n,m,ans;
int lowbit(int x)
{
return x&-x;
}
void ins(int x,int t)
{
for(int i=x; i<=n; i+=lowbit(i)) tr[i]+=t;
}
int query(int x)
{
int r=0;
for(int i=x; i; i-=lowbit(i)) r+=tr[i];
return r;
}
void print(int x)
{
if(x>n) printf("-1\n");else printf("%d\n",x);
}
int fin(int x)
{
int s=n+1,l=x+1,r=n,mx=query(x);
while(l<=r)
{
int mid=(l+r)>>1;
if(query(mid)>=mx*2) r=mid-1,s=mid;else l=mid+1;
}
return s;
}
int main()
{
freopen("challenge.in","r",stdin); freopen("challenge.out","w",stdout);
scanf("%d%d",&n,&m);
ans=n+1;
int sum=0;
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
ins(i,a[i]);
if(a[i]==sum) ans=min(ans,i);
sum+=a[i];
}
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y-a[x]),a[x]=y;
if (x>ans) printf("%d\n",ans);
else
{
ans=n+1;
if (query(x)==query(x-1)*2) ans=x;
else
{
while(x<=n)
{
int y=fin(x);
if (y==0) break;
if (query(y)==query(y-1)*2)
{
ans=y;
break;
}
x=y;
}
}
if (ans==n+1) printf("-1\n"); else printf("%d\n",ans);
}
}
return 0;
}